C++ Bridge Pattern Example

1. Definition

The Bridge Design Pattern decouples an abstraction from its implementation so that both can vary independently. It promotes the principle of composition over inheritance.

2. Problem Statement

When both an abstraction and its implementation need to have multiple variants, using traditional inheritance can create an explosion of classes. This results in a tight coupling between the abstraction and its implementation.

3. Solution

The solution involves creating two separate hierarchies: one for the abstraction and another for its implementation. The abstraction contains a reference to the implementation object and delegates the work to it.

4. Real-World Use Cases

1. GUI libraries where each OS provides a different implementation.

2. Remote controls for different devices.

3. Drivers for different types of databases.

5. Implementation Steps

1. Identify the two dimensions in your system that should be decoupled.

2. Create an abstract base class for the first dimension.

3. Create an abstract base class for the second dimension.

4. Link the two hierarchies by adding a member variable of the second dimension in the first dimension’s base class.

6. Implementation in C++

// Implementor
class Implementation {
public:
    virtual ~Implementation() {}
    virtual void operationImpl() = 0;
};
// Concrete Implementors
class ConcreteImplementationA : public Implementation {
public:
    void operationImpl() {
        std::cout << "ConcreteImplementationA operation" << std::endl;
    }
};
class ConcreteImplementationB : public Implementation {
public:
    void operationImpl() {
        std::cout << "ConcreteImplementationB operation" << std::endl;
    }
};
// Abstraction
class Abstraction {
protected:
    Implementation* implementation;
public:
    Abstraction(Implementation* impl) : implementation(impl) {}
    virtual ~Abstraction() {}
    virtual void operation() {
        implementation->operationImpl();
    }
};
// Refined Abstraction
class RefinedAbstraction : public Abstraction {
public:
    RefinedAbstraction(Implementation* impl) : Abstraction(impl) {}
    void operation() {
        std::cout << "Refined ";
        implementation->operationImpl();
    }
};
int main() {
    Implementation* implA = new ConcreteImplementationA();
    Abstraction* abstractionA = new RefinedAbstraction(implA);
    abstractionA->operation();
    Implementation* implB = new ConcreteImplementationB();
    Abstraction* abstractionB = new RefinedAbstraction(implB);
    abstractionB->operation();
    delete implA;
    delete abstractionA;
    delete implB;
    delete abstractionB;
    return 0;
}

Output:

Refined ConcreteImplementationA operation
Refined ConcreteImplementationB operation

Explanation:

1. The Implementation interface declares the operations for concrete implementations.

2. ConcreteImplementationA and ConcreteImplementationB provide specific implementations of the Implementation interface.

3. The Abstraction class contains a reference to an Implementation and delegates the work to it.

4. RefinedAbstraction is an extended version of the Abstraction, offering more nuanced control.

5. In the main function, two different Implementation objects are created and used with the RefinedAbstraction.

7. When to use?

Use the Bridge pattern when:

1. You want to avoid a permanent binding between an abstraction and its implementation.

2. Both the abstractions and their implementations should be extensible independently.

3. Changes in the implementation of an abstraction should have no impact on the clients.


Comments