Image for post

Bridge Design Pattern is a Structural Design Pattern used to decouple a class into two parts — abstraction and it’s implementation — so that both can be developed independently. This promotes the loose coupling between class abstraction & its implementation. You get this decoupling by adding one more level of indirection i.e. an interface which acts as a bridge between your original class & functionality. Insulation is another name of Bridge Design Pattern in C++ world.

“All problems in computer science can be solved by another level of indirection.” — David Wheeler

By the way, If you haven’t check out my other articles on Structural Design Patterns, then here is the list:

  1. Adapter
  2. Bridge
  3. Composite
  4. Decorator
  5. Facade
  6. Flyweight
  7. Proxy

The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like overridefinalpublic(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing “public:” sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.

Note:

  • If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic.
  • All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don’t have access to the latest compiler you can use https://wandbox.org/ which has preinstalled boost library as well.

/!: This article has been originally published on my blog. If you are interested in receiving my latest articles, please sign up to my newsletter.

Intent

To separate the interface from its implementation.

  • In other words, It’s all about connecting components together through flexible abstractions using aggregation/composition rather than inheritance/generalization.
  • This pattern involves an interface which acts as a bridge. That makes the functionality of concrete classes independent from interface implementer classes. Both types of classes can alter structurally without affecting each other.

Motivation for Bridge Design Pattern

  • Bridge Design Pattern prevents Cartesian Product complexity explosion. Don’t be scared with this mathematical term, I have simplified it with an example below.
  • So, for example, let’s suppose that you have some base class called Shape and then the Shape can be Circle or Square and it can also be drawn by API 1 or API 2.
struct DrawingAPI_1 { };
struct DrawingAPI_2 { };

struct Shape { virtual void draw() = 0; };
/* 2 x 2 scenario */
struct Circle : Shape, DrawingAPI_1 { };
struct Circle : Shape, DrawingAPI_2 { };
struct Square : Shape, DrawingAPI_1 { };
struct Square : Shape, DrawingAPI_2 { };
  • This way you end up having a two by two(2×2) scenario. So if you decide to implement it you have to implement four classes. One for you know the Circle with API_1Circle with API_2 and so on.
  • The Bridge Design Pattern is precisely the pattern that actually avoids this whole entity explosion.
  • So instead of having something like above, what we can do is we design the DrawingAPI interface(which later on used to derive API 1 & 2) and aggregate it in Circle & Square.

Bridge Design Pattern C++ Example

  • So following is the typical implementation of the Bridge Design Pattern. We are not going to look at anything quite so complicated here. But essentially a bridge is a mechanism that decouples the interface or the hierarchy from the implementation.
struct DrawingAPI {
    virtual void drawCircle() = 0;
};

struct DrawingAPI_1 : DrawingAPI {
    void drawCircle() { cout << "Drawn by API 1"<< endl; }
};
struct DrawingAPI_2 : DrawingAPI {
    void drawCircle() { cout << "Drawn by API 2"<< endl; }
};
struct Shape {
    Shape(DrawingAPI &drawingAPI) : m_drawingAPI{drawingAPI} {}
    virtual void draw() = 0;
protected:
    DrawingAPI &m_drawingAPI;   // Now Shapes does not need to worry about drawing APIs
};
struct Circle : Shape {
    Circle(DrawingAPI &drawingAPI) : Shape{drawingAPI} {}
    void draw() { m_drawingAPI.drawCircle(); }
};
int main() {
    DrawingAPI_1 API_1;
    DrawingAPI_2 API_2;
    Circle(API_1).draw();
    Circle(API_2).draw();
    return EXIT_SUCCESS;
}
  • This way you don’t rely as much as on inheritance and aggregation. Rather you rely on the interface.

#design-patterns #cpp #software-development #programming #coding

Bridge Design Pattern in Modern C++
4.40 GEEK