1 Elements of design patterns
- “Each pattern describes a recurring problem around us and the core of the solution to that problem. That way, you can use the solution again and again without having to reinvent the wheel. “The core of design patterns is that they provide relevant solutions that make it easier to reuse successful design benefits.
- Design patterns generally have four basic elements.
- (1) Mode name (patern name). A mnemonic that uses one or two words to describe a pattern’s problem, solution effect. Naming a new pattern adds to the design vocabulary. Design patterns allow design at a high level of abstraction. With a schema vocabulary, you can discuss schemas and use them when writing documents. Pattern names help people think and communicate design ideas and results to others. Finding the right schema name is also one of the difficulties in designing patterns.
- (2) The problem. The question describes when patterns should be used. It explains the design problem and the cause and effect of its existence, and may describe specific design problems, such as how to represent algorithms with objects. It may also describe a class or object structure that causes inactivity. Sometimes, the questions section includes a list of prerequisites that must be met to use the pattern.
- (3) Solutions. The solution describes the components of the design, their interrelationships, and their respective responsibilities and ways of collaborating. Because patterns are like a template, they can be applied to many different situations. So the solution does not describe a particular concrete design or implementation, but rather provides an abstract description of the design problem and how to solve it with a generic combination of elements (classes or object combinations).
- (4) Consequences. Effects describe the effects of applying patterns and the trade-offs that should be made when using patterns. Although pattern effects are not always mentioned when describing design decisions, they are important for evaluating design choices and understanding the costs and benefits of using patterns. Software effects are mostly concerned with measuring time and space, and they also address language and implementation issues. Because reuse is one of the elements of object-oriented design, pattern effects include their impact on the flexibility, extensibility, or portability of the system. Listing these effects explicitly is helpful in understanding and evaluating patterns.
- The design pattern identifies the classes and instances to be included, their roles, how they collaborate, and the assignment of responsibilities. Each design pattern focuses on a specific object-oriented design problem or design point, describing when to use it, whether to use it in the context of some other design, and how to use it and how to trade off. According to the purpose of design patterns can be divided into three categories, as shown in the table.
- The creation pattern is concerned with object creation; Structural patterns deal with combinations of classes or objects; Behavioral patterns describe how classes or objects interact and assign responsibilities.
2. Creative design patterns
- Creative patterns abstract the instantiation process, and they help a system be independent of the objects it creates, combines, and represents. A class creation pattern uses inheritance to change the class being instantiated, while an object creation pattern delegates instantiation to another object.
- As systems evolve to rely more on object composition than class inheritance, the creation pattern becomes more important. When this happens, the focus shifts from hard-coding a fixed set of behaviors to defining a smaller set of basic behaviors that can be combined into any number of more complex behaviors. Creating objects with specific behaviors in this way requires more than just instantiating a class.
- There are two recurring themes in these patterns. First, they encapsulate information about which specific classes the system uses. Second, they hide how instances of these classes are created and put together. All the system knows about these objects is the interface defined by the abstract class. Thus, the creative pattern gives great flexibility in what is created, who creates it, how it is created, and when it is created. They allow you to configure a system with “production” objects that differ greatly in structure and functionality. Configuration can be static (that is, specified at compile time) or dynamic (at run time).
2.1 Abstract Factory
- 1) intent
- Provides an interface for creating a series of related or interdependent objects without specifying their concrete classes.
- 2) structure
- The structure of the abstract factory pattern is shown in figure 1.
- Among them:
- AbstractFactory declares an operation interface for creating abstract product objects.
- ConcreteFactory Implements the creation of concrete product objects.
- AbstractProduct declares an interface for a class of product objects.
- ConcreteProduct Defines a product object that will be created by the corresponding concrete factory, implementing the AbstractProduct interface.
- The Client uses only interfaces declared by AbstractFactory and AbstractProduct classes.
- 3) the applicability
- The Abstract Factory pattern applies to:
- A system is independent of the creation, composition, and presentation of its products.
- When a system is to be configured by one of multiple product families.
- When you want to emphasize the design of a series of related product objects for joint use.
- When a product class library is provided, only their interfaces, not implementations, are displayed.
- The Abstract Factory pattern applies to:
2.2 Builder
- 1) intent
- Separating the construction of a complex object from its representation allows the same construction process to create different representations.
- 2) structure
- The structure of the generator pattern is shown below.
- Among them:
- Builder specifies the abstract interface for the parts that create a Product object.
- The ConcreteBuilder interface implements the Builder to construct and assemble the parts of the product, define and specify the representation it creates, and provide an interface to retrieve the product.
- The Director constructs an object that uses the Builder interface.
- Product represents a complex object being constructed. The ConcreteBuilder creates an internal representation of the product and defines its assembly process. Contains classes that define components, including interfaces that assemble them into the final product.
- 3) the applicability
- Builder mode applies to:
- When algorithms that create complex objects should be independent of the components of the object and how they are assembled.
- When the construction process must allow different representations of the object being constructed.
- Builder mode applies to:
2.3 Factory Method
- 1) intent
- Define an interface for creating objects and let subclasses decide which class to instantiate. The Factory Method delays the instantiation of a class to its subclasses.
- 2) structure
- The structure of the factory method pattern is shown in figure 1.
- Among them:
- Builder mode applies to:
- Product defines the interface to the object created by the factory method.
- ConcreteProduct implements the Product interface.
- Creator declares the factory method, which returns an object of type Prodiuet. Creator can also define a default implementation of a factory method that returns a default ConcreteProduet object that can be called to create a Product object.
- The ConcreteCreator redefines the factory method to return a ConcreteProduct instance.
- 3) Applicability:
- The Factory Method pattern applies to:
- When a class does not know the class of the object it must create.
- When a class wants its subclasses to specify the objects it creates.
- When a class delegates the responsibility of creating an object to one of multiple help subclasses, and you want to localize which help subclass is the agent.
- The Factory Method pattern applies to:
2.4 Prototype
- 1) intent
- Specify what kind of objects to create with prototype instances, and create new objects by copying these prototypes.
- 2) structure
- The structure of the prototype pattern is shown below.
- Among them:
- Prototype declares an interface that copies itself.
- ConcretePrototype Implements an operation that copies itself.
- The Client lets a prototype copy itself to create a new object.
- 3) the applicability
- The Prototype pattern applies to:
- When a system should be created, constructed, and represented independently of its production.
- When the class to be instantiated is specified at run time, for example, by dynamic loading.
- To avoid creating a factory class hierarchy parallel to the product class hierarchy.
- When an instance of a class can have only one of several different combinations of states. It may be more convenient to create a proportionate number of prototypes and clone them than to manually instantiate the class each time in the appropriate state.
- The Prototype pattern applies to:
2.5 Singleton(Singleton)
- 1) intent
- Ensure that a class has only one instance and provide a global access point to access it.
- 2) structure
- The structure of the singleton pattern is shown in figure 1.
- Singleton refers to an Iinstance operation that allows customers to access only one instance of it. Ihnstance is a class operation that may be responsible for creating its own unique instance.
- 3) the applicability
- The Singleton pattern applies to:
- When a class can have only one instance and a customer can access it from a well-known access point.
- When the unique instance should be extensible by subclassing and the customer can use an extended instance without changing the code.
- The Singleton pattern applies to:
2.6 Comparison of creation patterns
- There are two common ways to parameterize the system by classes used for those objects created by the system: generating children of the class that created the object and methods that parameterize the system. The former corresponds to using the Factory Method pattern, with the main disadvantage that you may need to create a new subclass just to change the product class. This change can cascade, for example, if the creator of a product was itself created by a factory method, then its creator must also be redefined. The latter relies more on composition of objects, defining an object responsible for specifying the class of the product object and taking it as a parameter to the system. This is a key feature of the Abstract Factory, Builder, and Pototype patterns, which all involve creating a new “Factory object” responsible for creating product objects. The Abstract Factory produces multiple objects from this Factory object. The Builder builds a complex product step by step using a relatively complex protocol from this factory object. Prototype The factory object creates the production object by copying the Prototype object. In this case, the factory object and the prototype are the same object, since the prototype is responsible for returning the product object.
3. Structural design mode
- The structural design pattern deals with how classes and objects are combined to achieve a larger structure. The structural class pattern uses inheritance mechanisms to compose interfaces or implementations. A simple example is to combine more than two classes into a single class using multiple inheritance, resulting in a class that contains all the properties of its parent class. This pattern is especially helpful for multiple independently developed libraries to work together. One example is the Adapter pattern in class form. In general, adapters make one interface compatible with other interfaces, giving a unified abstraction of multiple different interfaces. To do this, the class adapter privately inherits an Adaptee class. In this way, the adapter can represent its interface with the interface of adaptee.
- Rather than combining interfaces and implementations, the structural object pattern describes ways in which objects can be combined to implement new functionality. Because object composition relationships can be changed at run time, there is more flexibility in object composition than is possible with static class composition.
- The Composite pattern is an example of the structural object pattern. It describes how to construct a class hierarchy consisting of the corresponding classes of two types of objects. The composite objects allow users to combine primitive objects and other composite objects to form arbitrarily complex structures. In the Proxy pattern, Proxy objects serve as a convenient substitute or placeholder for other objects. It can be used in many ways, for example to represent an object in a remote address space in a local space, a larger object that needs to be loaded, or to protect access to sensitive objects. The Proxy pattern also provides a degree of indirect access to some of the specific properties of an object, which can be restricted, enhanced, or modified. The Flyweight pattern defines a structure for sharing objects. Object sharing is required for at least two reasons: efficiency and consistency. The object sharing mechanism of Flyweight focuses on the spatial efficiency of objects. Applications that use many objects must consider the overhead of each object. Using object sharing instead of object replication can save a lot of space resources. But these objects can be shared only if they have no context-specific state defined. Flyweight objects have no such state. Any additional information needed to perform a task is passed on only when needed. Because there is no context-dependent state, Flyweight objects can be freely shared.
- While the Flyweight pattern illustrates how to generate many smaller objects, the Facade pattern describes how to represent an entire subsystem with a single object. A facade in a pattern is used to represent a set of objects, and the facade’s responsibility is to forward messages to the objects it represents. The Bridge pattern separates the abstraction of an object from its implementation so that they can be changed independently.
- The Decorator pattern describes how to dynamically add responsibilities to objects. The Decorator pattern is a structural pattern that combines objects recursively, allowing you to add as many object responsibilities as you want. For example, a Decorator object that contains a user interface component can add decorations like borders or shadows to the component, or it can add functions like window scrolling and zooming to the component. You can simply add two decorators by nesting one Decorator object inside another, as well as adding other decorators. Therefore, each Decorator object must be compatible with its component’s interface and be guaranteed to deliver messages to it. The Decorator pattern does its job (such as drawing the border of a component) either before or after forwarding a message. Many structural patterns are correlated to some degree.
3.1 Adapter
- 1) intent
- Translate the interface of a class into another interface that the customer wants. The Adapter pattern enables classes to work together that would otherwise not work together due to interface incompatibilities.
- 2) structure
- A class adapter uses multiple inheritance to match one interface with another, as shown in the diagram.
- The object adapter relies on object composition, and its structure is shown in figure 1.
- Among them:
- Target defines the domain-specific interfaces used by clients.
- Client works with objects that match the Target interface.
- Adaptee defines an existing interface that needs to be adapted.
- Adapter Adaptee the Target interface and Adaptee interface.
- 3) the applicability
- Adapter mode applies to:
- You want to use an existing class whose interface does not meet the requirements.
- You want to create a edible class that can work with other unrelated classes or unexpected classes (that is, classes whose interfaces may not be compatible).
- (For object Adapter only) You want to use an existing subclass, but it is not possible to subclass each one to match their interface. An object adapter can be adapted to its superclass interface.
- Adapter mode applies to:
3.2 Bridge
- 1) intent
- Separate the abstract parts from their implementation parts so that they can all change independently.
- 2) structure
- The structure of the bridge mode is shown in figure 1.
- Among them:
- Interface that defines an abstract class and maintains a pointer to an object of type Implementor.
- RefinedAbstraction Expands the interface defined by Abstraction.
- Implermentor defines the interface for the implementation class, which does not have to be exactly the same as the Abstraction interface; In fact, the two interfaces can be completely different. Generally, the Implementor interface only provides the basic operations, while Abstraction defines the higher-level operations based on those basic operations.
- ConcreteImplementor Implementor and defines an implementation of the ConcreteImplementor.
- 3) the applicability
- Bridge mode applies to:
- You don’t want to have a fixed binding relationship between an abstraction and its implementation. This may be the case, for example, because the implementation part should be selected or switched at runtime.
- The abstraction of a class, as well as its implementation, should be augmented by methods that generate subclasses. This is the Bridge pattern that allows developers to combine different abstract interfaces and implementation parts and extend them separately.
- Changes to an abstract implementation part should have no impact on the customer, that is, the customer code does not have to be recompiled.
- (C++) wants to completely hide the abstract implementation from the customer.
- There are many classes to generate class hierarchies.
- You want to share the implementation across multiple objects (possibly using reference counting), but at the same time require the customer to be unaware of this.
- Bridge mode applies to:
3.3 Composite
- 1) intent
- Group objects into a tree structure to represent a “whole of parts” hierarchy. Composite allows users to use single objects and Composite objects consistently.
- 2) structure
- The structure of the composite pattern is shown in figure 1.
- Among them:
- Component declares interfaces for the objects in the composition; Implement the default behavior of interfaces common to all classes where appropriate: Declare an interface for accessing and managing child components of Component: (Optional) Define an interface in a recursive structure for accessing a parent Component, and implement it as appropriate.
- Leaf represents the Leaf node object in the combination, Leaf node has no children, and defines the behavior of the primitive object in the combination.
- Composite defines the behavior of those components that have child components; Store subcomponents; Implement actions related to child components in the Component interface.
- The Client manipulates composite Component objects through the Component interface.
- 3) the applicability
- Composite mode applies to:
- You want to represent part of a global hierarchy of objects.
- The user is expected to ignore the difference between a composite object and a single object, and the user will use all objects in the composite structure uniformly.
- Composite mode applies to:
3.4 Decorator
- 1) intent
- Add some extra responsibilities to an object dynamically. The Decorator pattern is more flexible than subclassing in terms of adding functionality.
- 2) structure
- The structure of decorator mode is shown in figure 1.
- Among them:
- Component defines an interface to objects to which responsibilities can be dynamically added.
- ConcreteComponent Defines an object to which you can add responsibilities.
- The Decorator maintains a pointer to the Component object and defines an interface consistent with the Component interface.
- The ConcreteDecorator adds responsibilities to the component.
- 3) the applicability
- The Decorator pattern applies to:
- Add responsibilities to a single object dynamically and transparently without affecting other objects.
- Deal with responsibilities that can be undone.
- When it is not possible to extend by subclass generation. In one case, there may be a large number of independent extensions, and the number of subclasses to support each combination will explode. Another case may be that the class definition is hidden or cannot be used to generate subclasses.
- The Decorator pattern applies to:
3.5 Facade
- 1) intent
- Providing a consistent interface for a set of interfaces in a subsystem, the Facade pattern defines a high-level interface that makes the subsystem easier to use.
- 2) structure
- The structure of the facade pattern is shown in figure 1.
- Among them:
- Facade knows which subsystem classes are responsible for handling requests.
- Subsystem classes to implement the function of the Subsystem; Process tasks assigned by Facade objects; There is no information about Facade.
- 3) the applicability
- The Facade pattern applies to:
- When it comes to providing a simple interface for a complex subsystem, the subsystem tends to become more and more complex as it evolves. Most patterns produce more and smaller classes when used, which makes subsystems more reusable and easier to customize, but also creates some difficulties for users who don’t need to customize subsystems. A Facade can provide a simple default view that is sufficient for most users, while those requiring more customization can bypass the Facade layer.
- There is a significant dependency between the client program and the implementation part of the abstract class. Introducing a Facade to separate this subsystem from customers and other subsystems improves subsystem independence and portability.
- When a hierarchical subsystem needs to be built, the Facade pattern is used to define the entry points for each layer in the subsystem. If subsystems are interdependent, they can communicate only through the Facade, simplifying their dependencies.
- The Facade pattern applies to:
3.6 Flyweight(Enjoy element)
- 1) intent
- Use sharing techniques to effectively support a large number of fine-grained objects.
- 2) structure
- The structure of the share mode is shown in figure 1.
- Among them:
- Flyweight describes an interface through which Flyweight can be accepted and used for external states.
- ConcreteFlyweight implements the Flyweight interface and adds storage space for internal states (if any).
- The ConcreteFlyweight object must be shareable. The state it stores must be internal, that is, it must be independent of the ConcreteFlyweight object’s scene.
- Not all Flyweight subclasses need to be shared. The Flyweight interface makes sharing possible, but it does not enforce it. In some level of Flyweight objects structure, UnsharedConcreteFlyweight object will usually ConcreteFlyweight object as a child node.
- FlyweightFactory creates and manages Flyweight objects; To ensure that Flyweight is properly shared, when a user requests a Flyweight, the FlyweightFactory object provides a created instance or creates one if one does not exist.
- The Client maintains a reference to the Flyweight; Calculates or stores the external state of one or more flyweights.
- 3) the applicability
- Flyweight mode works for:
- An application uses a large number of objects.
- Because of the large number of objects, there is a lot of storage overhead.
- Most of the state of an object can be changed to an external state.
- If you remove an object’s external state, you can replace many groups of objects with relatively few shared objects.
- Applications do not rely on object identifiers. Because Flyweight objects can be shared, identity tests return true for objects that are conceptually distinct.
- Flyweight mode works for:
3.7 Proxy
- 1) intent
- Provide a proxy for other objects to control access to that object.
- 2) structure
- The structure of the proxy pattern is shown in figure 1.
- Among them:
- The Proxy stores a reference so that the Proxy can access the entity; Provide an interface identical to the Subject’s interface so that proxies can be used instead of entities; Controls access to the entity and may be responsible for creating and deleting it; Other functions depend on the type of Proxy: Remote Proxy is responsible for encoding requests and their parameters and sending encoded requests to entities in different address Spaces; Virtual Proxy can cache additional information about an entity to delay access to it: Protection Proxy checks whether the caller has the access necessary to implement a request.
- Subiect defines a common interface between RealSubject and Proxy so that proxies can be used anywhere RealSubject is used.
- RealSubject defines the entities represented by the Proxy.
- 3) the applicability
- The Proxy mode is applicable to situations where generic and complex object Pointers are needed to replace simple Pointers:
- Remote Proxy provides data for an object in different address Spaces does not represent.
- Virtual proxies create expensive objects on demand.
- Protection Proxy controls access to raw objects when objects should have different access rights.
- Smart References replace simple Pointers and perform additional operations when accessing objects. Typical uses include: counting references to an actual object so that it can be released automatically when there are no references to it; The first time a persistent object is referenced, it is loaded into memory; Before accessing an actual object, check that it is locked to ensure that other objects cannot change it.
- The Proxy mode is applicable to situations where generic and complex object Pointers are needed to replace simple Pointers:
3.8 Comparison of structural patterns
- The Adapter pattern and the Bridge pattern have some functional characteristics that both provide a degree of indirection to another object, thus benefiting system flexibility, and both involve forwarding requests to that object from an interface other than itself. The Adapter pattern is primarily designed to solve the problem of mismatches between two existing interfaces, regardless of how those interfaces are implemented or how each of them might evolve. This approach allows them to work together without having to redesign either of the two independently designed classes. The Bridge pattern Bridges an abstract interface with its (possibly multiple) implementation parts. Although this pattern lets users modify the classes that implement it, it still provides a stable interface for users and ADAPTS to new implementations as the system evolves. Adapter and Bridge patterns are commonly used at different stages of the software life cycle for different problems. The Adapter pattern is implemented after the class has been designed; The Bridge pattern is implemented before designing the class.
- The Composite pattern and the Decorator pattern have a similar structure, indicating that they both organize a variable number of objects based on recursive composition. Decorators are designed to be able to add responsibilities to objects without having to subclass them, which avoids the rapid increase in subclasses that would result from implementing all the combination of functions statically. Composite aims to construct classes so that multiple related objects can be handled in a uniform manner, while multiple objects can be treated as one object, with emphasis on presentation. The two are usually used together.
- Both the Decorator pattern and the Proxy pattern describe how to provide some degree of indirect reference to objects. The Proxy pattern forms an object and provides a consistent interface to the user. Unlike the Decorator pattern, the Proxy pattern cannot add or separate properties dynamically, and is not designed for recursive composition. It emphasizes a relationship (between a Proxy and its entities) that can be expressed statically. The purpose is to provide a substitute for an entity when direct access to the entity is inconvenient or undesirable, for example, if the entity is on a remote device, access is restricted, or the entity is persisted. In the Proxy pattern, the entity defines key functionality, and the Proxy provides (or denies) access to it. In the Decorator pattern, components provide only part of the functionality, and one or more decorators perform the other functions. The Decorator pattern is suitable for situations where the full functionality of an object cannot, orat least is inconvenient, be determined at compile time.