Bridge Pattern (Handle/Body) (scheme implementation)
Contents
- 1. General information. The need to use the Bridge pattern. Figure
- 2. The structure of the pattern. Figure. Pattern participants
- 3. Application results (based on Figure)
- 4. Features of the pattern implementation
- 5. Program text in C++. Struct implementation
- 6. Applying the Bridge Pattern
- Related topics
Search other resources:
1. General information. The need to use the Bridge pattern. Figure
The Bridge pattern refers to patterns that structure objects. The main purpose of the pattern is to separate the abstraction from the implementation so that both the abstraction and the implementation can be changed independently.
If you need to implement an abstraction in several ways, then inheritance is usually used. In this case, there is an abstract class at the top of the hierarchy that contains the abstraction’s common interface. This class inherits more specific classes that provide a specific implementation.
However, this approach is not always flexible. The reason for this is the strict binding of the implementation to the abstraction. As a consequence, independent modification and the ability to expand and reuse are complicated.
For example. Let an abstract class AbstractClass be given with two concrete implementations ImpClass1, ImpClass2, as shown in Figure 1-a. Suppose there is a need for each specific implementation to add a new abstract one. This capability is provided by introducing the AbstractSubClass abstract class. Then, for this feature, one should additionally introduce support for implementations of the classes ImpClass1, ImpClass2. Accordingly, the introduced classes will have the names ImpSubClass1, ImpSubClass2 (Figure 1-b). This approach is inconvenient and requires redefining subclass implementations.
Figure 1. Abstraction extension by adding classes AbstractSubClass, ImpSubClass1, ImpSubClass2
Also, the disadvantage of the approach depicted in Figure 1 is that the client code becomes dependent on a particular implementation. When an ImpClass1 object is created, the abstraction is bound to its implementation, which means that the client is guided by this particular implementation. This makes it more difficult to change the client implementation. This leads to the need to recompile the client code.
⇑
2. The structure of the pattern. Figure. Pattern participants
The structure of the Bridge pattern is shown in Figure 2.
Figure 2. The structure of Bridge pattern
Let’s give a brief description of the classes of this structure that are participants in the pattern.
- Abstract class Abstraction is some kind of abstraction that defines the abstraction interface. This class stores a reference (pointer) to the object of type Implementor. Concrete implementations of abstractions (RefinedAbstraction) are inherited from this class.
- The RefinedAbstraction class is a concrete implementation of an abstraction that extends Abstraction.
- Abstract class Implementor is an interface for classes of concrete implementations (ConcreteImplementorA, ConcreteImplementorB). This class does not have to follow the Abstraction class interface (in general, the Abstraction and Implementor interfaces may differ). Typically, the Implementor class defines only primitive operations. At the same time, the Abstraction class defines higher-level operations based on the use of the Implementor class operations.
- The ConcreteImplementorA, ConcreteImplementorB classes implement the Implementor interface. These are specific implementations.
⇑
3. Application results (based on Figure)
The literature defines the following results of applying the Bridge pattern.
- Separation of implementation from interface. An implementation is not permanently bound to the interface. The implementation of the abstraction can be configured at runtime. There is no compile-time dependency between the Abstraction and Implementor classes. You don’t need to recompile the Abstraction class and its clients to change the implementation.
Another advantage of separating the implementation from the interface is that the system is layered. Therefore, the structure of the system is improved. The higher level parts of the system only know about the Abstraction and Implementor classes.
- The degree of extensibility of the system is increased. The Abstraction and Implementor class hierarchies are independent, so they can be easily extended.
- Hiding implementation details from clients.
⇑
4. Features of the pattern implementation
Depending on the structure of the classes, the following main points can be distinguished when implementing the Bridge pattern.
- If the task condition requires the presence of only one Implementor class, it may not be necessary to make this class abstract. This is a degenerate case of the relation between Abstraction and Implementor classes.
- The choice of the Implementor object to create can be done in the constructor of the Abstraction class. To do this, the constructor must have information about the concrete classes that implement the Implementor interface. The method of choosing a particular class is determined by the condition of the problem and can be implemented in different ways.
- With the correct construction of the code, it is possible to achieve a distribution of implementers. This means that the same implementation can be used by multiple objects. To make this possible, you need to enter a reference count to the object and check the number of references in the reference assignment operation (operator=()).
⇑
5. Program text in C++. Struct implementation
The example shows the implementation of the Bridge pattern, the structure of which is shown in Figure 2.
// The Bridge pattern #include <iostream> using namespace std; // A class that is an implementation interface class Implementor abstract { public: virtual void OperationImp() abstract; }; // Concrete implementation 1 class ConcreteImplementorA : public Implementor { public: void OperationImp() override { // some work is being done here cout << "ConcreteImplementorA::OperationImp()" << endl; } }; // Concrete implementation 2 class ConcreteImplementorB : public Implementor { public: void OperationImp() override { // some work cout << "ConcreteImplementorB::OperationImp()" << endl; } }; // A class that is an abstraction interface class Abstraction abstract { private: Implementor* imp = nullptr; public: // Constructor - gets the implementation number Abstraction(int numImp) { // The type of implementation is formed if (numImp == 1) imp = new ConcreteImplementorA; else imp = new ConcreteImplementorB; } // An operation void Operation() { imp->OperationImp(); } // Destructor virtual ~Abstraction() { if (imp) delete imp; } }; // Refined abstraction class RefinedAbstraction : public Abstraction { public: // Constructor RefinedAbstraction(int numImp) : Abstraction(numImp) { } }; void main() { // Client - contains a reference to the abstraction Abstraction* obj = nullptr; // Menu int numImp; cout << "Enter Implementation (1-2):" << endl; cin >> numImp; // Create an abstraction obj = new RefinedAbstraction(numImp); // Call the operation obj->Operation(); if (obj) delete obj; }
⇑
6. Applying the Bridge Pattern
The Bridge pattern is used in the following cases.
- When it is necessary to avoid the constant binding of abstraction to implementation. An example of this would be the need to select an implementation during program execution.
- When the task involves the extension of both abstraction and implementation by new subclasses. The Bridge pattern allows you to combine different abstractions and implementations, as well as change them independently of each other.
- Changes to the implementation of the abstraction should not affect clients. That is, the client code should not be recompiled.
- If you need to completely hide the implementation of the abstraction from clients.
- In the case when, when making changes to the condition of the problem, the number of abstraction classes begins to increase. Here it becomes necessary to divide the hierarchy into two parts.
- When it is necessary to hide from the client the division of a single implementation between several objects.
⇑
Related topics
- Pattern Adapter. Review and research. Examples of implementation in C++
- Composite pattern. Implementation of structure in C++
- Facade pattern. Implementation of structure in C++
⇑