设计模式(7) 桥接模式

  • 桥接模式的概念与实现
  • 为何叫桥接模式
  • 桥接模式的适用场景

继承是面向对象的三大特性之一,但不少时候使用继承的结果却不尽如人意。除了人尽皆知的紧耦合问题外,有的时候还会致使子类的快速膨胀。设计模式

设想这样一个场景:最初设计的时候有一个类型Product,但后来随着新需求的出现,X缘由致使了它的变化,X有两种状况,则经过继承须要建立两个新的子类ProductX1,ProductX2,但后来有出现了Y因素也会致使Product的变化,若是Y有三种状况,则会出现ProductX1Y1,ProductX1Y2,ProductX1Y3...等,一共2*3=6个类。ide

使用这种继承的方式,若是再出现新的变化因素,或者某个变化因素出现了新的状况,都会致使子类的快速膨胀,给维护带来很大的挑战。this

形成这个问题的根本缘由是类型在沿着多个维度变化。为了应对变化,通常会经过抽象的方法,找到其中比较稳定的部分,而后抽象其行为,令客户程序依赖于抽象而不是具体实现。一样的道理,当一个类型同时受到多个因素变化的影响时,也经过把每一个因素抽象,让类型依赖于一系列抽象因素的办法尽可能处理这个问题,这即是桥接模式解决问题的思路。设计

桥接模式的概念与实现

GOF对桥接模式的描述为:
Decouple an abstraction from its implementationso that the two can vary independently.
— Design Patterns : Elements of Reusable Object-Oriented Software
桥接模式将抽象部分与它的实现部分分离,使它们均可以独立地变化。code

桥接模式的UML类图为
桥接模式 UML类图对象

示例代码:blog

public interface IImpl
{
    void OperationImpl();
}

public interface IAbstraction
{
    IImpl Implementor { get; set; }
    void Operation();
}
public class ConcreteImplementatorA : IImpl
{
    public void OperationImpl()
    {
        ...
    }
}

public class ConcreteImplementatorB : IImpl
{
    public void OperationImpl()
    {
        ...
    }
}

public class RefinedAbstration : IAbstraction
{
    public IImpl Implementor { get; set; }

    public void Operation()
    {
        Implementor.OperationImpl();
    }
}

这样子看起来仍是比较抽象,再举个具体的例子汽车-道路,目前汽车有小汽车、巴士两类,路有水泥路、石子路两类,这样“车在路上行驶”就会有四种状况,这个场景用桥接模式来描述的话能够是:
汽车类的抽象与实现:继承

public interface IVehicle
{
    string Drive();
}

public class Car : IVehicle
{
    public string Drive()
    {
        return "Car";
    }
}

public class Bus : IVehicle
{
    public string Drive()
    {
        return "Bus";
    }
}

经过桥接模式关联道路与汽车:get

public abstract class Road
{
    protected IVehicle vehicle;
    public Road(IVehicle vehicle)
    {
        this.vehicle = vehicle;
    }

    public abstract string DriveOnRoad();
}

public class UnpavedRoad : Road
{
    public UnpavedRoad(IVehicle vehicle) : base(vehicle) { }
    public override string DriveOnRoad()
    {
        return vehicle.Drive() + " is on Unpaved Road";
    }
}

public class CementRoad : Road
{
    public CementRoad(IVehicle vehicle) : base(vehicle) { }
    public override string DriveOnRoad()
    {
        return vehicle.Drive() + " is on Cement Road";
    }
}

调用:string

IVehicle vehicle = new Car();
Road road = new CementRoad(vehicle);
Console.WriteLine(road.DriveOnRoad());
// Car is on Cement Road
Speed speed = new FastSpeed(road);
Console.WriteLine(speed.DriveWithSpeed());
// Car is on Cement Road,

在这里Road依赖的是IVehivle抽象,具体的汽车实如今调用的时候决定。
对比直接继承出四种类型的方式,这样作的好处貌似并不明显,仍是须要四个类,并且更复杂,但若是汽车或者道路类型继续增长,或者引入了别的变化因素,状况就不同了。

为何叫桥接模式

有个疑惑是关于桥接模式的名称的,为何叫桥接模式呢?以前的工厂、适配器等名称都挺形象的,但桥接模式好像有点不一样,这要从桥接模式解决问题的思路提及,桥接模式更多的是提示咱们面向对象的设计分解方式,能够归纳为三步:

  • 第一步,把依赖具体变成依赖抽象。
  • 第二步,若是对象同时沿着多个维度变化,那就顺次展开抽象因素。
  • 第三步,为每一个抽象因素提供具体实现。

示意图:
分解

到了第三步,就能够大概看出桥接模式的形象化表示了,IX、IY和IZ构成链接部(被称为支座),而每一个具体类造成了一个个桥墩。
因此桥接模式,是以抽象之间的依赖为桥面、以具体实现为桥墩,建起了一座链接需求与实现的桥梁。

桥接模式的适用场景

  • 若是一个系统须要在构件的抽象化角色和具体化角色之间增长更多的灵活性,避免在两个层次之间创建静态的联系。
  • 设计要求实现化角色的任何改变不该当影响客户端,或者说实现化角色的改变对客户端是彻底透明的。
  • 一个构件有多于一个的抽象化角色和实现化角色,系统须要它们之间进行动态耦合。
  • 虽然在系统中使用继承是没有问题的,可是因为抽象化角色和具体化角色须要独立变化,设计要求须要独立管理这二者。

参考书籍: 王翔著 《设计模式——基于C#的工程化实现及扩展》

相关文章
相关标签/搜索