- Introduction Design Patterns(book-pdf)
- Design Patterns : Elements of Reusable Object-Oriented Software
-
In general, design patterns prevent the repetition of code, which include everything, such as dependencies, code structure, contracts, names and specifications, etc., anything you can think of.
In software engineering, a design pattern describes a relatively small, well-defined aspect (i.e. functionality) of a computer program in terms of how to write the code. Using a pattern is intended to leverage an existing concept rather than re-inventing it. This can decrease the time to develop software and increase the quality of the resulting program. Some consider using patterns to be best practice for software design. Some consider using design patterns as a structured approach to computer programming.
Conceptually, design pattern may be described as more specific than programming paradigm and less specific than algorithm.
- Name
- Problem
- Solution
- Consequences and trade-os of application
- Codify good design
- Distill and disseminate experience
- Aid to novices and experts alike
- Abstract how to think about design
- Give design structures explicit names
- Common vocabulary
- Reduced complexity
- Greater expressiveness
- Capture and preserve design information
- Articulate design decisions succinctly
- Improve documentation
- Facilitate restructuring/refactoring
- Patterns are interrelated
- Additional
- flexibility
In general, design patterns can speed up the development process, and the reuse of design patterns can prevent errors and bugs that are time-consuming to identify, and the use of design patterns makes the code more readable for developers. become another
Creational design patterns are design patterns which focus on creating objects effectively. They bring flexibility to the software design and help in the reusability of existing code.Creational design patterns solve this problem by somehow controlling this object creation. are composed of two dominant ideas.
-
Encapsulating Knowledge About Concrete Classes:
- Imagine you're building a game with various characters, like dogs and cats. Traditionally, you might have code scattered throughout your application that directly creates
DogorCatobjects. This approach makes your code tightly coupled to these specific classes. - Creational patterns advocate for encapsulating this knowledge of which concrete classes are used within a central location. This can be achieved through techniques like factory methods or abstract factories. These factories become responsible for creating the specific objects based on your request, shielding the rest of your code from the underlying implementation details.
- Imagine you're building a game with various characters, like dogs and cats. Traditionally, you might have code scattered throughout your application that directly creates
-
Hiding How Objects Are Created and Combined:
- Often, the process of creating objects can be complex, involving multiple steps or decisions based on certain conditions. Exposing these intricacies in your code can make it hard to reason about and modify.
- Creational patterns address this by hiding the object creation logic behind a well-defined interface. This allows you to use the objects without worrying about the specifics of their instantiation. For instance, the builder pattern lets you construct complex objects step-by-step using a fluent interface, separating the object creation process from its usage.
Summary
Creational patterns are ones that create objects, rather than having to instantiate objects directly. This gives the program more flexibility in deciding which objects need to be created for a given case. There are mainly 6 types of creational design patterns: Factory Method, Abstract Factory, Singleton, Prototype, Builder and Object Pool pattern.
creates an instance of several derived classes belonging to a single family of objects. However, employment of this pattern, as with similar design patterns, may result in unnecessary complexity and extra work in the initial writing of code.
-
Summary : a class requests the objects it requires from a factory object instead of creating the objects directly
-
Example: Suppose we go to an ice cream factory and order a chocolate ice cream. When we place an order, the shopkeeper sends the order to the kitchen and we get it in a few minutes when the ice cream is ready. We are not concerned with what happens inside the kitchen and what ingredients are used to make the ice cream. The internal call by the shopkeeper to create a vanilla, chocolate, or strawberry ice cream is hidden from the user.
It may be used to solve problems such as:
- How can an application be independent of how its objects are created?
- How can a class be independent of how the objects that it requires are created?
- How can families of related or dependent objects be created?
The pattern describes how to solve such problems:
- Encapsulate object creation in a separate (factory) object by defining and implementing an interface for creating objects.
- Delegate object creation to a factory object instead of creating objects directly. This makes a class independent of how its objects are created. A class may be configured with a factory object, which it uses to create objects, and the factory object can be exchanged at runtime.
-
Abstract Factory: This is an interface or an abstract class that declares a set of factory methods for creating a family of related objects.
-
Concrete Factory: These are the implementations of the abstract factory interface. Each concrete factory is responsible for creating a specific family of related objects.
-
Abstract Products: These are interfaces or abstract classes that declare the common set of methods for the objects within a family.
-
Concrete Products: These are the actual implementations of the abstract products. Each concrete product is specific to a particular family and is created by a corresponding concrete factory.
-
Client: The client code interacts with the abstract factory and abstract product interfaces.
Structure:
Better Understanding:
- Example Ts
- Example JavaScript
- Example Java This example was created by ai
- Example Python
The Builder Design Pattern is a creational pattern used in software design to construct a complex object step by step. It allows the construction of a product in a step-by-step fashion, where the construction process can vary based on the type of product being built. The pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
Simple and understandable explanation:
Imagine constructing a complex object, like a car. Traditionally, you might define a constructor with numerous parameters for engine type, transmission, features, etc. This approach becomes cumbersome with many optional parameters and can lead to cluttered code.
The Builder Pattern introduces a dedicated "Builder" class. This class offers methods for setting each of the object's attributes independently. Users can then configure the object by chaining these methods on the builder, specifying the desired values. Finally, a "build" method on the builder assembles the final object based on the provided configuration.
Summary : The Builder Design Pattern offers a structured approach to constructing complex objects, promoting better code organization, improved readability, and increased flexibility in creating object variations.
The Builder design pattern solves problems like
- How can a class (the same construction process) create different representations of a complex object?
- How can a class that includes creating a complex object be simplified?
The Builder design pattern describes how to solve such problems:
- Encapsulate creating and assembling the parts of a complex object in a separate Builder object.
- A class delegates object creation to a Builder object instead of creating the objects directly. A class (the same construction process) can delegate to different Builder objects to create different representations of a complex object.
Imagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. Such initialization code is usually buried inside a monstrous constructor with lots of parameters. Or even worse: scattered all over the client code.
For example, let’s think about how to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a bigger, brighter house, with a backyard and other goodies (like a heating system, plumbing, and electrical wiring)?
The simplest solution is to extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually you’ll end up with a considerable number of subclasses. Any new parameter, such as the porch style, will require growing this hierarchy even more.
There’s another approach that doesn’t involve breeding subclasses. You can create a giant constructor right in the base House class with all possible parameters that control the house object. While this approach indeed eliminates the need for subclasses, it creates another problem.
In most cases most of the parameters will be unused, making the constructor calls pretty ugly. For instance, only a fraction of houses have swimming pools, so the parameters related to swimming pools will be useless nine times out of ten.
The Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called builders.
The pattern organizes object construction into a set of steps (buildWalls, buildDoor, etc.). To create an object, you execute a series of these steps on a builder object. The important part is that you don’t need to call all of the steps. You can call only those steps that are necessary for producing a particular configuration of an object.
Some of the construction steps might require different implementation when you need to build various representations of the product. For example, walls of a cabin may be built of wood, but the castle walls must be built with stone.
-
Product: It may consist of multiple components or parts, and its structure can vary based on the implementation.The Product is typically a class with attributes representing the different parts that the Builder constructs. It has various ingredients (attributes) that come together to form the complete product.
-
Builder: It typically includes methods for constructing individual parts of the product.By defining an interface, the Builder allows for the creation of different concrete builders that can produce variations of the product. It outlines the steps (methods) needed to add each ingredient (set attributes) to create the final product.
-
**ConcreteBuilder:**Each ConcreteBuilder is tailored to create a specific variation of the product.It keeps track of the product being constructed and provides methods for setting or constructing each part. This is like a specific version of the recipe.
-
Director (Optional): It collaborates with a Builder, but it doesn’t know the specific details about how each part of the object is constructed. It provides a high-level interface for constructing the product and managing the steps needed to create the complex object. This is like a helpful friend who follows the recipe (Builder) and guides you (the Client) through the steps. They might suggest using a specific ConcreteBuilder depending on your preference. (Not always used)
-
Client: It creates a Builder object and passes it to the Director to initiate the construction process. The Client may retrieve the final product from the Builder after construction is complete.
The Builder design pattern is used when you need to create complex objects with a large number of optional components or configuration parameters. This pattern is particularly useful when an object needs to be constructed step by step, some of the scenarios where the Builder design pattern is beneficial are:
- Building complex objects: If you're dealing with objects that have many parts, some of which might be optional, the Builder Pattern helps separate the construction process from the final object itself. This makes the code cleaner and easier to understand.
- Taking it step-by-step: When the construction of an object involves a step-by-step process where different configurations or options need to be set at different stages.
- **Ditching the parameter overload:**If your constructor is getting overloaded with too many parameters, the Builder Pattern offers a solution. It breaks down the configuration into smaller, more manageable steps.
- Immutable Objects: When you want to create immutable objects, and the Builder pattern allows you to construct the object gradually before making it immutable.
- Creating object variations: Do you need to create the same object type with different configurations? The Builder Pattern allows you to define different "builders" that specialize in specific variations, making configuration more flexible and readable.
-
Simple Objects: For objects with just a few parameters and a straightforward construction process, a simple constructor or a static factory method is more efficient. The Builder Pattern might be overkill in these cases.
-
Performance is Crucial: If your application prioritizes speed, the extra steps involved in the Builder Pattern (method calls, object creation) could impact performance, especially for frequently constructed objects. Consider simpler approaches for performance-critical scenarios.
-
Already Immutable: If you're using a language with built-in support for immutable objects (like Java's
finalkeyword) and the object structure is simple, constructors or static factories might be sufficient. -
Unnecessary Complexity: Adding a Builder class for every complex object can clutter your codebase. If the object is simple and doesn't benefit from a step-by-step construction process, a simpler approach might be better.
-
Tightly Coupled: If the Builder class is intricately linked to the object it constructs (changes in the object require changes in the builder), it can reduce code flexibility and maintainability. Consider looser coupling for better maintainability.
- Allows you to vary a product's internal representation.
- Encapsulates code for construction and representation.
- Provides control over the steps of the construction process.
- A distinct ConcreteBuilder must be created for each type of product.
- Builder classes must be mutable.
- May hamper/complicate dependency injection.
Structure*
Better Understanding
The Factory Method Design Pattern is a creational design pattern that provides an interface for creating objects in a superclass, allowing subclasses to alter the type of objects that will be created. It encapsulates object creation logic in a separate method, promoting loose coupling between the creator and the created objects. This pattern is particularly useful when the exact types of objects to be created may vary or need to be determined at runtime, enabling flexibility and extensibility in object creation.
Summary : Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
Simple explanations for better understanding:
Imagine you're ordering pizza. You call a pizzeria (the superclass), which has a menu (the interface). But the pizzeria itself doesn't make all the pizzas (it doesn't use the "new" operator directly).
Instead, the pizzeria has different branches (the subclasses) that specialize in specific pizzas. Each branch has its own recipe (factory method) for creating pizzas (objects). You tell the pizzeria what kind you want (runtime decision), and it forwards the order to the appropriate branch to make it.
The factory method pattern relies on inheritance, as object creation is delegated to subclasses that implement the factory method to create objects.
The Factory Method design pattern solves problems like
- How can an object be created so that subclasses can redefine its subsequent and distinct implementation?
- How can an object's instantiation be deferred to a subclass?
The Factory Method design pattern describes how to solve such problems:
- Define a factory method within the superclass that defers the object's creation to a subclass's factory method.
- Create an object by calling a factory method instead of directly calling a constructor.
Imagine that you’re creating a logistics management application. The first version of your app can only handle transportation by trucks, so the bulk of your code lives inside the Truck class.
After a while, your app becomes pretty popular. Each day you receive dozens of requests from sea transportation companies to incorporate sea logistics into the app.
Great news, right? But how about the code? At present, most of your code is coupled to the Truck class. Adding Ships into the app would require making changes to the entire codebase. Moreover, if later you decide to add another type of transportation to the app, you will probably need to make all of these changes again.
As a result, you will end up with pretty nasty code, riddled with conditionals that switch the app’s behavior depending on the class of transportation objects.
The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Don’t worry: the objects are still created via the new operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as products.
At first glance, this change may look pointless: we just moved the constructor call from one part of the program to another. However, consider this: now you can override the factory method in a subclass and change the class of products being created by the method.
NOTE: The code that uses the factory method (often called the client code) doesn’t see a difference between the actual products returned by various subclasses. The client treats all the products as abstract Transport. The client knows that all transport objects are supposed to have the deliver method, but exactly how it works isn’t important to the client.
- When you want to encapsulate object creation: If you have a complex object creation process or if the process may vary based on conditions, encapsulating this logic in a factory method can simplify client code and promote reusability.
- When you want to decouple client code from concrete classes: Using the Factory Method Pattern allows you to create objects through an interface or abstract class, abstracting away the specific implementation details of the concrete classes from the client code. This promotes loose coupling and makes it easier to modify or extend the system without impacting existing client code.
- When you need to support multiple product variations: If your application needs to create different variations of a product or if new types of products may be introduced in the future, the Factory Method Pattern provides a flexible way to accommodate these variations by defining factory methods for each product type.
- When you want to support customization or configuration: Factories can be used to encapsulate configuration logic, allowing clients to customize the creation process by providing parameters or configuration options to the factory method.
1. Creator (Abstract Class or Interface):
- Factory Method Definition: This central method dictates the object creation process. It adheres to the principle of least astonishment by specifying the return type as the product interface or abstract class, ensuring consistency for client code. Client code interacts solely with this interface to request object creation.
- Encapsulation of Additional Functionality (Optional): The creator can encapsulate methods relevant to the products it creates, promoting code organization, reusability, and a centralized location for product-related logic.
2. Concrete Creator (Subclasses of Creator):
- Implementation of the Factory Method: Subclasses provide the specific logic for creating their designated concrete product objects. This implementation details object instantiation, configuration, and any additional steps specific to the concrete product type.
- Decoupling Client Code: Concrete creators act as a shield, insulating client code from the intricacies of concrete product creation. This promotes loose coupling, simplifies client interactions, and fosters a more maintainable codebase.
3. Product (Interface or Abstract Class):
- Definition of the Common Interface: This component outlines the operations that all concrete product objects can perform. It enforces a standard for all products, ensuring compatibility with the factory method and enabling interchangeable usage of concrete products within the system.
- Facilitation of Loose Coupling: By relying solely on the product interface, client code can interact with various concrete product implementations without needing to be aware of their specific details. This promotes loose coupling and simplifies modifications or extensions to the product hierarchy.
4. Concrete Product (Implementations of Product):
- Representation of the Actual Objects: These classes embody the functionality of the specific products being created. They implement the methods defined in the product interface or extend the product abstract class, providing the concrete behavior for each product type.
- Support for Product Variations: Concrete product classes enable the creation of diverse product types that adhere to the defined product interface. This allows for a flexible system that can accommodate future product additions without modifying existing client code or the factory structure.
Structure
Better Understanding
You are developing a software system for an e-commerce platform that deals with various types of products. Each product category (e.g., electronics, clothing, books) requires specific handling during creation. However, you want to decouple the client code from the concrete product creation logic to enhance flexibility and maintainability. Additionally, you want to allow for easy extension by adding new product types in the future without modifying existing code. source
- Prototype:
- Singleton:
- Object Pool:
Structural patterns concern class and object composition. They use inheritance to compose interfaces and define ways to compose objects to obtain new functionality.
- Adapter allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class.
- Bridge decouples an abstraction from its implementation so that the two can vary independently.
- Composite composes zero-or-more similar objects so that they can be manipulated as one object.
- Decorator dynamically adds/overrides behavior in an existing method of an object.
- Facade provides a simplified interface to a large body of code.
- Flyweight reduces the cost of creating and manipulating a large number of similar objects.
- Proxy provides a placeholder for another object to control access, reduce cost, and reduce complexity.
Most behavioral design patterns are specifically concerned with communication between objects.
- Chain of responsibility delegates commands to a chain of processing objects.
- Command creates objects that encapsulate actions and parameters.
- Interpreter implements a specialized language.
- Iterator accesses the elements of an object sequentially without exposing its underlying representation.
- Mediator allows loose coupling between classes by being the only class that has detailed knowledge of their methods.
- Memento provides the ability to restore an object to its previous state (undo).
- Observer is a publish/subscribe pattern, which allows a number of observer objects to see an event.
- State allows an object to alter its behavior when its internal state changes.
- Strategy allows one of a family of algorithms to be selected on-the-fly at runtime.
- Template method defines the skeleton of an algorithm as an abstract class, allowing its subclasses to provide concrete behavior.
- Visitor separates an algorithm from an object structure by moving the hierarchy of methods into one object.
-
Active Object : Decouples method execution from method invocation that reside in their own thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
-
Balking: Only execute an action on an object when the object is in a particular state.
-
Binding properties: Combining multiple observers to force properties in different objects to be synchronized or coordinated in some way.
-
Compute kernel:The same calculation many times in parallel, differing by integer parameters used with non-branching pointer math into shared arrays, such as GPU-optimized Matrix multiplication or Convolutional neural network.
-
Double-checked locking : Reduce the overhead of acquiring a lock by first testing the locking criterion (the 'lock hint') in an unsafe manner; only if that succeeds does the actual locking logic proceed. Can be unsafe when implemented in some language/hardware combinations. It can therefore sometimes be considered an anti-pattern.
-
Event-based asynchronous Addresses problems with the asynchronous pattern that occur in multithreaded programs.
-
Guarded suspension: Manages operations that require both a lock to be acquired and a precondition to be satisfied before the operation can be executed. No —
-
Join Join-pattern provides a way to write concurrent, parallel and distributed programs by message passing. Compared to the use of threads and locks, this is a high-level programming model. No —
-
Lock One thread puts a "lock" on a resource, preventing other threads from accessing or modifying it.[22] No PoEAA[14]
-
Messaging design pattern (MDP) Allows the interchange of information (i.e. messages) between components and applications. No —
-
Monitor object An object whose methods are subject to mutual exclusion, thus preventing multiple objects from erroneously trying to use it at the same time. Yes —
-
Reactor A reactor object provides an asynchronous interface to resources that must be handled synchronously. Yes —
-
Read-write lock Allows concurrent read access to an object, but requires exclusive access for write operations. An underlying semaphore might be used for writing, and a Copy-on-write mechanism may or may not be used. No —
-
Scheduler Explicitly control when threads may execute single-threaded code. No —
-
Service handler pattern For each request, a server spawns a dedicated client handler to handle a request.[23] Also referred to as thread-per-session.[24] No —
-
Thread pool A number of threads are created to perform a number of tasks, which are usually organized in a queue. Typically, there are many more tasks than threads. Can be considered a special case of the object pool pattern. No —
-
Thread-specific storage Static or "global" memory local to a thread. Yes —
-
Safe Concurrency with Exclusive Ownership Avoiding the need for runtime concurrent mechanisms, because exclusive ownership can be proven. This is a notable capability of the Rust language, but compile-time checking isn't the only means, a programmer will often manually design such patterns into code - omitting the use of locking mechanism because the programmer assesses that a given variable is never going to be concurrently accessed.
-
CPU atomic operation x86 and other CPU architectures support a range of atomic instructions that guarantee memory safety for modifying and accessing primitive values (integers). For example, two threads may both increment a counter safely. These capabilities can also be used to implement the mechanisms for other concurrency patterns as above. The C# language uses the Interlocked class for these capabilities.













.webp)

