Open In App

Bridge Method | C++ Design Patterns

Last Updated : 08 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Bridge Pattern is basically a structural design pattern in software engineering or in C++ programming that is used to separate an object’s abstraction from its implementation. It is part of the Gang of Four (GoF) design patterns and is particularly useful when we need to avoid a permanent binding between an abstraction and its implementation. The Bridge Pattern promotes flexibility and allows us to change both the abstraction and the implementation independently.

Use Cases of the Bridge Pattern in C++ Design Patterns

  • Remote Controls: Consider a remote control application that needs to support multiple devices (e.g., TVs, DVD players, stereos) and various remote control protocols (e.g., infrared, Bluetooth).
  • Platform-Independent Graphics Libraries: The Bridge pattern is often used in graphics libraries to provide platform-independent abstractions for drawing shapes, lines, and text while allowing different implementations for various operating systems (e.g., Windows, Linux, macOS).
  • Database Abstraction: When we are working with databases, the Bridge pattern can help separate the database-specific code from the application’s business logic. This allows us to switch between different database management systems (e.g., MySQL, PostgreSQL, SQLite) without affecting the core application.
  • GUI Frameworks: GUI frameworks often use the Bridge pattern to separate the GUI components (e.g., buttons, text fields) from the underlying platform-specific implementation (e.g., Windows API, GTK, Qt).
  • File System Abstraction: When developing file management software, we can use the Bridge pattern to abstract the file system operations, separating them from the underlying file system implementations (e.g., NTFS, ext4, HFS+).
  • Printer Driver Software: Printer driver software can benefit from the Bridge pattern to separate the printer’s generic features (e.g., page formatting, print job management).

Example for Bridge Design Pattern in C++:

Let’s assume that if we want to create a drawing program where we have different shapes (e.g., circles and squares) and different rendering methods (e.g., vector and raster). The Bridge pattern can help us to separate the shapes (abstraction) from the rendering methods (implementation).

Implementation of Bridge Pattern in C++:

Abstraction:

Shape (Abstract Class) is defined as an abstract class with a pure virtual function draw(). This represents the abstraction part of the Bridge Pattern.

C++




// Abstraction: Shape
class Shape {
public:
    virtual void draw() = 0;
};


Renderer is another abstract class with a pure virtual function render(). This represents the implementation part of the Bridge Pattern.

C++




// Implementations: Renderer (VectorRenderer and RasterRenderer)
class Renderer {
public:
    virtual void render() = 0;
};
 
class VectorRenderer : public Renderer {
public:
    void render() override {
        std::cout << "Rendering as a vector\n";
    }
};
 
class RasterRenderer : public Renderer {
public:
    void render() override {
        std::cout << "Rendering as a raster\n";
    }
};


Concrete Abstractions:

Circle and Square are concrete shape classes derived from the Shape abstract class.They each have a member of type Renderer to hold a reference to the renderer implementation.Circle and Square are concrete shape classes derived from the Shape abstract class.They each have a member of type Renderer to hold a reference to the renderer implementation.

C++




class Circle : public Shape {
public:
    Circle(Renderer& renderer) : renderer(renderer) {}
 
    void draw() override {
        std::cout << "Drawing a circle ";
        renderer.render();
    }
 
private:
    Renderer& renderer;
};
 
class Square : public Shape {
public:
    Square(Renderer& renderer) : renderer(renderer) {}
 
    void draw() override {
        std::cout << "Drawing a square ";
        renderer.render();
    }
 
private:
    Renderer& renderer;
};


Main Function:

In the main() function, instances of VectorRenderer and RasterRenderer are created.Instances of Circle and Square are also created, each associated with a specific renderer.Calling draw() on Shapes circle.draw() is called, which internally calls the VectorRenderer to render the circle, and square.draw() is called, which internally calls the RasterRenderer to render the square.

C++




int main() {
    VectorRenderer vectorRenderer;
    RasterRenderer rasterRenderer;
 
    Circle circle(vectorRenderer);
    Square square(rasterRenderer);
 
    circle.draw();  // Output: Drawing a circle Rendering as a vector
    square.draw();  // Output: Drawing a square Rendering as a raster
 
    return 0;
}


Below is the complete combined code of the above example:

C++




#include <iostream>
 
// Abstraction: Shape
class Shape {
public:
    virtual void draw() = 0;
};
 
// Implementations: Renderer (VectorRenderer and
// RasterRenderer)
class Renderer {
public:
    virtual void render() = 0;
};
 
class VectorRenderer : public Renderer {
public:
    void render() override
    {
        std::cout << "Rendering as a vector\n";
    }
};
 
class RasterRenderer : public Renderer {
public:
    void render() override
    {
        std::cout << "Rendering as a raster\n";
    }
};
 
// Concrete Abstractions: Circle and Square
class Circle : public Shape {
public:
    Circle(Renderer& renderer)
        : renderer(renderer)
    {
    }
 
    void draw() override
    {
        std::cout << "Drawing a circle ";
        renderer.render();
    }
 
private:
    Renderer& renderer;
};
 
class Square : public Shape {
public:
    Square(Renderer& renderer)
        : renderer(renderer)
    {
    }
 
    void draw() override
    {
        std::cout << "Drawing a square ";
        renderer.render();
    }
 
private:
    Renderer& renderer;
};
 
int main()
{
    VectorRenderer vectorRenderer;
    RasterRenderer rasterRenderer;
 
    Circle circle(vectorRenderer);
    Square square(rasterRenderer);
 
    circle.draw(); // Output: Drawing a circle Rendering as
                   // a vector
    square.draw(); // Output: Drawing a square Rendering as
                   // a raster
 
    return 0;
}


Output

Drawing a circle Rendering as a vector
Drawing a square Rendering as a raster

Key components of Bridge Patterns in C++:

  • Abstraction: This is the high-level part of the system or code that defines the interface for the client code. Basically, it contains a reference to an implementation object but does not implement the behavior itself. The abstraction defines methods and properties that the client code can use.
  • Refined Abstraction: These are concrete classes that extend the abstraction and provide specific implementations of the high-level interface. Refined abstractions are what the client code interacts with.
  • Implementation: This is the low-level part of the system or code that defines the interface for implementing specific functionality. The implementation is separate from the abstraction and can be developed independently. It may have multiple implementations, each of which can be used by different refined abstractions.
  • Concrete Implementation: These are basically the concrete classes that implement the low-level interface. Each concrete implementation provides a specific way of performing the underlying functionality. There can be multiple concrete implementations for different platforms, databases, or technologies.

Working and explanation of Example by using Key Component:

In the above example:

  • Abstractions: Shape” is the abstraction, representing different shapes that can be drawn.
  • Implementations: Renderer” is the implementation, representing different rendering methods.
  • Concrete Abstraction : “Circle” and “Square” are concrete abstractions, and they take a reference to a “Renderer” object, allowing you to draw the shape using different rendering methods.

Diagrammatic Representation of the Example

2023-11-02_12-24-43

Diagrammatical Representation of the Example

  • Abstraction: This is the high-level component in the code that defines the abstract interface. It may include methods, properties, or any other behaviors that the client code can use. The Abstraction typically has a reference to an implementation object.
  • Refined Abstraction: This is a subclass of the Abstraction that can extend or modify the behavior of the Abstraction. Refined Abstractions provide additional functionality by building on the basic Abstraction.
  • Implementation: This is the low-level component that defines the concrete implementation of the Abstraction’s interface. It is often an interface itself, and various Concrete Implementations can be created to provide different implementation strategies.
  • Concrete Implementation: These are classes that implement the Implementation interface. Concrete Implementations contain the actual code that carries out the specific details of the implementation.

Advantages of the Bridge Design Pattern in C++ Design Patterns

  • Scalability: When our system needs to handle a growing number of abstractions and implementations, the Bridge Pattern provides a scalable way to manage and organize these combinations.
  • Decouples Abstraction and Implementation: One of the primary advantages of the Bridge Pattern is that it decouples the abstraction (the high-level part of the system) from its implementation (the low-level part). This separation allows changes in one part of the system to have minimal impact on the other.
  • Enhances Extensibility: The Bridge Pattern makes it easier to add new abstractions or implementations without modifying the existing code. This extensibility is particularly useful when dealing with complex systems.
  • Improved Maintainability: After the changes to the abstraction and implementation are isolated, the code becomes easier to maintain. so we can modify or extend each part without affecting the other, reducing the chances of introducing bugs.
  • Supports Platform Independence: The Bridge Pattern is useful when we need to support multiple platforms or databases. we can create platform-specific implementations and switch between them without altering the abstraction code.
  • Simplifies Client Code: Clients using the Bridge Pattern can work with the abstraction without needing to know the implementation details. This simplifies the client code and reduces its complexity.

Disadvantages of the Bridge Design Patterns in C++ Design Patterns

  • Overhead: The Bridge pattern can introduce some overhead due to the need for multiple classes and interactions between them. This can affect performance in certain situations, although in most cases, the impact is negligible and not a significant concern.
  • Increased Complexity: Implementing the Bridge Pattern can introduce additional classes and relationships between them. This added complexity may make the code harder to understand, especially for simple scenarios. It’s important to use the Bridge Pattern when the benefits outweigh this added complexity.
  • Increased Number of Classes: The Bridge Pattern can lead to a larger number of classes in our codebase. If not managed well, this proliferation of classes can make the system harder to navigate and maintain.
  • Complexity of Managing Multiple Implementations: If we have a large number of abstractions and implementations, managing all the combinations and ensuring they work correctly can be challenging. This may lead to increased testing and maintenance efforts.
  • Potential for Misuse: While the Bridge Pattern is useful in specific situations, it can be misapplied in cases where simpler solutions would suffice. Using the pattern when it’s not necessary can lead to unnecessary complexity and decreased code maintainability.

Conclusion

So Basically, the Bridge Pattern allows us to create a hierarchy of abstractions and implementations. we can change or extend the implementation independently of the abstraction, and the client code can work with different implementations without needing to change its code. This makes it easier to add new features, support multiple platforms, or maintain and extend your software.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads