The Bridge Design Pattern is a structural design pattern that separates the abstraction .......

The Bridge Design Pattern is a structural design pattern that separates the abstraction .......

Bridge Design Pattern

The Bridge Design Pattern is a structural design pattern that separates the abstraction (high-level control logic) from the implementation (low-level operational details). This allows both to evolve independently and provides flexibility in switching between implementations without affecting the abstraction.


1. Concept

In the Bridge Pattern:


  • Abstraction defines the high-level functionality.
  • Refined Abstraction extends the abstraction to add more features or variations.
  • Implementor defines the interface for the implementation.
  • Concrete Implementor provides specific implementations for the Implementor.



Why Use the Bridge Pattern?


  1. Decoupling: It separates abstraction from implementation, allowing independent evolution.
  2. Flexibility: You can add new abstractions or implementations without modifying the existing code.
  3. Scalability: Reduces the complexity of managing multiple abstraction-implementation combinations.



2. Bridge Pattern Structure

The Bridge Pattern typically involves the following components:


  1. Abstraction:
  2. Refined Abstraction:
  3. Implementor:
  4. Concrete Implementors:



Example Scenario: Shape and Area Calculators

We will use a scenario where:


  • The abstraction is a shape (IShape).
  • The implementor is an area calculator (IAreaCalculator).
  • Concrete implementations will calculate the area for different shapes (CircleAreaCalculator, RectangleAreaCalculator, SquareAreaCalculator).



3. Step-by-Step Implementation

Step 1: Define the Implementor Interface

The IAreaCalculator interface specifies a method for calculating the area.

// Implementor Interface
public interface IAreaCalculator
{
    double CalculateArea(params double[] dimensions);
}        

Explanation:


  • The IAreaCalculator interface defines the contract for area calculation.
  • It uses params double[] dimensions to handle varying numbers of parameters for different shapes (e.g., one for a circle, two for a rectangle).



Step 2: Create Concrete Implementations

These classes implement the IAreaCalculator interface and define the behavior for calculating the area of specific shapes.

Concrete Implementor: CircleAreaCalculator

// Concrete Implementor
public class CircleAreaCalculator : IAreaCalculator
{
    public double CalculateArea(params double[] dimensions)
    {
        double radius = dimensions[0];
        return Math.PI * radius * radius;
    }
}        


Concrete Implementor: RectangleAreaCalculator

// Concrete Implementor
public class RectangleAreaCalculator : IAreaCalculator
{
    public double CalculateArea(params double[] dimensions)
    {
        double width = dimensions[0];
        double height = dimensions[1];
        return width * height;
    }
}        


Concrete Implementor: SquareAreaCalculator

// Concrete Implementor
public class SquareAreaCalculator : IAreaCalculator
{
    public double CalculateArea(params double[] dimensions)
    {
        double side = dimensions[0];
        return side * side;
    }
}         

Explanation:


  • Each class provides a unique implementation of CalculateArea for its respective shape.
  • The method signature is identical (as required by the IAreaCalculator interface).



Step 3: Define the Abstraction

The IShape abstraction defines a contract for shapes and uses an IAreaCalculator to calculate the area.

// Abstraction
public interface IShape
{
    double CalculateArea();
}        

Explanation:


  • The IShape abstraction provides a high-level interface for calculating the area.
  • It does not directly handle the calculations—it delegates them to an IAreaCalculator.



Step 4: Create Refined Abstractions

These classes implement the IShape interface and use an IAreaCalculator to calculate the area for specific shapes.

Refined Abstraction: Circle

// Refined Abstraction
public class Circle(double radius, IAreaCalculator areaCalculator) : IShape
{
    private readonly double _radius = radius;
    private readonly IAreaCalculator _areaCalculator = areaCalculator;

    public double CalculateArea()
    {
        return _areaCalculator.CalculateArea(_radius);
    }
}        

Refined Abstraction: Rectangle

// Refined Abstraction
public class Rectangle(double width, double height, IAreaCalculator areaCalculator) : IShape
{
    private readonly double _width = width;
    private readonly double _height = height;
    private readonly IAreaCalculator _areaCalculator = areaCalculator;

    public double CalculateArea()
    {
        return _areaCalculator.CalculateArea(_width, _height);
    }
}        

Refined Abstraction: Square

// Refined Abstraction
public class Square(double side, IAreaCalculator areaCalculator) : IShape
{
    private readonly double _side = side;
    private readonly IAreaCalculator _areaCalculator = areaCalculator;

    public double CalculateArea()
    {
        return _areaCalculator.CalculateArea(_side);
    }
}        

Explanation:


  • Each refined abstraction represents a specific shape and uses an IAreaCalculator to calculate its area.
  • The calculation logic is completely decoupled from the shape's definition.



Step 5: Use the Bridge in the Main Program

The client code demonstrates how the abstraction (IShape) works with different implementations (IAreaCalculator).

public class Program
{
    public static void Main()
    {
        // Using RectangleAreaCalculator with a Rectangle
        IShape rectangle = new Rectangle(5, 10, new RectangleAreaCalculator());
        Console.WriteLine($"Rectangle Area: {rectangle.CalculateArea()}");

        // Using CircleAreaCalculator with a Circle
        IShape circle = new Circle(7, new CircleAreaCalculator());
        Console.WriteLine($"Circle Area: {circle.CalculateArea()}");

        // Using SquareAreaCalculator with a Square
        IShape square = new Square(4, new SquareAreaCalculator());
        Console.WriteLine($"Square Area: {square.CalculateArea()}");
    }
}        

Output:

Rectangle Area: 50
Circle Area: 153.93
Square Area: 16        

Explanation:


  • The IShape abstraction interacts with different IAreaCalculator implementations.
  • You can swap out one IAreaCalculator for another without modifying the shape classes.



4. Key Points


1. Shared Interface for Consistency:

· The IAreaCalculator ensures all implementations have the same method structure (CalculateArea).

· This allows the abstraction (IShape) to interact consistently with any calculator.

2. Behavioural Differences: the internal logic of each IAreaCalculator differs based on the geometry (circle, rectangle, square).

3. Decoupling: The abstraction (IShape) does not depend on specific implementations of IAreaCalculator.

4. Flexibility: You can add new shapes or calculators without modifying existing code, adhering to the Open/Closed Principle.


5. Advantages of the Bridge Pattern


  1. Decoupling: Separates abstraction from implementation.
  2. Scalability: Allows new abstractions and implementations to be added independently.
  3. Maintainability: Easier to manage and modify components.




Conclusion

The Bridge Design Pattern is a powerful technique for decoupling abstraction from implementation. By enforcing consistent structure through interfaces and allowing variations ibehaviouror, it enables flexible, scalable, and maintainable systems.


source code: https://github.com/karwanessmat/CSharpDesignPatterns/tree/master/2_Structural%20Design%20Patterns/Bridge/Bridge4Demo



#csharp #dotnet #programming #softwaredevelopment #designpattern #BridgePattern

要查看或添加评论,请登录

Karwan Essmat的更多文章

社区洞察

其他会员也浏览了