继承是面向对象的三大特性之一,但不少时候使用继承的结果却不尽如人意。除了人尽皆知的紧耦合问题外,有的时候还会致使子类的快速膨胀。设计模式
设想这样一个场景:最初设计的时候有一个类型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类图为
对象
示例代码: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#的工程化实现及扩展》