Bridge 桥接模式简介与 C# 示例【结构型2】【设计模式来了_7】

发布时间 2023-10-09 17:20:27作者: 橙子家

〇、简介

1、什么是桥接模式?

一句话解释:

  通过一个类的抽象,与另一个类的抽象关联起来,当做桥。此后不管两个抽象类的实现有多少种,均可以通过这个桥来将两个对象联系起来。

桥接,顾名思义就是用桥来连接河两岸,将原本不关联的两部分联系起来,且不影响两岸的各自演化,演化出来的不同对象仍可以通过这个桥连接。

桥接设计模式是一种结构型设计模式,它通过将抽象与其实现分离来实现松耦合。这种模式可以使得抽象和实现可以独立地进行扩展,而不会相互影响。

官方意图描述:将抽象部分与它的实现部分分离,使它们可以独立地变化。

一个比喻:(班主任为学生订餐)

  学生和餐品相当于两个变化的类,均可以增加或减少,老师就相当于桥接模式中的桥。学生想吃什么套餐,可以通过老师来对应到具体的套餐类别。

2、优缺点和适用场景

  • 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
  • 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
  • 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。
  • 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。

适用场景:

  • “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
  • 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
  • 不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统。

实际适用场景:

  • 游戏开发:在游戏开发中,可以使用桥接模式将游戏引擎和场景分离开来,使得用户可以在不同的场景之间进行切换,而不需要修改游戏引擎的代码。
  • 网络编程:在网络编程中,可以使用桥接模式将协议和实现分离开来,使得用户可以在不同的协议之间进行切换,而不需要修改实现的代码。
  • 图形界面开发:在图形界面开发中,可以使用桥接模式将控件的样式和布局分离开来,使得用户可以在不同的样式和布局之间进行切换,而不需要修改代码。

参考:https://zhuanlan.zhihu.com/p/58903776

一、代码示例

首先一起看下不使用桥接模式的示例

假设我们正在开发一个图形绘制应用程序,需要支持不同类型的图形(例如圆形、正方形)以及不同类型的绘制方式(例如红色、蓝色)。在不使用桥接设计模式的情况下,我们可能会创建以下类层次结构:

// 图形基类
abstract class Shape
{
    public abstract void Draw();
}
// 圆形
class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}
// 正方形
class Square : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a square");
    }
}
// 红色绘制器
class RedColor : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing in red color");
    }
}
// 蓝色绘制器
class BlueColor : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing in blue color");
    }
}

在上述代码中,我们有一个基类 Shape 和两个派生类 Circle 和 Square,分别表示圆形和正方形。另外,我们还有两个派生类 RedColor 和 BlueColor,分别表示红色和蓝色的绘制器。这种实现方式存在一个问题:当我们需要绘制不同颜色的图形时,必须在每个具体图形类中添加相应颜色的绘制方法。这导致了类层次结构的爆炸增长,使得代码难以维护和扩展。

使用桥接设计模式,我们可以将图形和颜色的实现分离开来。如下示例代码,我们定义一个新的抽象类 Color,表示颜色的实现,在 Shape 类中,将颜色的实现作为一个成员变量,并在 Draw 方法中调用颜色的 ApplyColor 方法:

class Program
{
    static void Main(string[] args) // 测试一下
    {
        Color red = new Red();
        Color blue = new Blue();
        Shape redCircle = new Circle(red); // 通过将不同的颜色实例传递给图形类的构造函数来绘制不同颜色的图形
        Shape blueSquare = new Square(blue);
        redCircle.Draw();
        blueSquare.Draw();

        Console.ReadLine();
    }
}
// 颜色基类
abstract class Color
{
    public abstract void ApplyColor();
}
class Red : Color // 红色
{
    public override void ApplyColor()
    {
        Console.WriteLine("Applying red color");
    }
}
class Blue : Color // 蓝色
{
    public override void ApplyColor()
    {
        Console.WriteLine("Applying blue color");
    }
}

// 修改后的图形基类
abstract class Shape // (目标接口,定义了客户端期望的操作:Draw)
{
    protected Color color; // 将颜色的实现作为一个成员变量
    public Shape(Color color)
    {
        this.color = color;
    }
    public abstract void Draw();
}
class Circle : Shape // 圆形
{
    public Circle(Color color) : base(color) // (被适配者 Color 的引用)
    { }
    public override void Draw()
    {
        Console.Write("Drawing a circle. ");
        color.ApplyColor(); // 在 Draw 方法中调用颜色的 ApplyColor 方法
    }
}
class Square : Shape // 正方形
{
    public Square(Color color) : base(color)
    { }
    public override void Draw()
    {
        Console.Write("Drawing a square. ");
        color.ApplyColor(); // 在 Draw 方法中调用颜色的 ApplyColor 方法
    }
}

通过使用桥接设计模式,我们成功地将图形和颜色的实现解耦,使得它们可以独立地进行扩展。

如果后续需求增加,需要加一个颜色黄色,然后图形加一个椭圆,此时就可以分别再实现两个抽象类(Color、Shape)即可。

二、桥接模式的结构

Abstraction:定义抽象类的接口,维护一个指向 Implementor 类型对象的指针。

Implementor:定义实现类的接口,该接口不一定要与 Abstraction 的接口完全一致,事实上这两个接口可以完全不同。一般来讲,Implementor 接口仅提供进本操作,而 Abstraction 则定义了基于这些基本操作的较高层次的操作。

ConcreteInplementor:实现 Implementor 接口并定义它的具体实现。

三、相关模式

AbstractFactory 模式可以用来创建和配置一个特定的 Bridge 模式。

Adapter 模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge 模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。