1. 意图
将抽象部分与它的实现部分分离, 使它们可以独立地变化
2. 别名
Handle / Body
3. 动机
当一个抽象可能有多个实现时, 通常用继承来协调他们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。
4. 适用性
1) 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为在程序运行时刻实现部分可以被选择或者切换
2) 类的抽象和实现都可以通过生成子类的方法加以扩充。
5. 结构
6. 代码示例
个人认为Bridge模式是非常简单,同时又是非常能体现OO精髓的一个设计模式。所谓设计中的“尽量使用聚合而不是继承”,大部分时候用Bridge模式就可以解决问题
考虑到下面一个场景,有一个抽象的基类AbstractCar, 有子类SUV , Car(小轿车), 与卡车(Truck), 还有后轮驱动与四驱,柴油车,汽油车,混合动力,纯电动,排量(1.6, 2.0,2.4), 如果按照继承的实现方式, 要有多少个类? 组合一下就是好几十种
class Program
{
static void Main(string[] args)
{
}
}
abstract class AbstractCar
{
public abstract void Go();
public abstract void FuelCharging();
}
abstract class SUV : AbstractCar
{
}
abstract class DieselEngineSUV : SUV
{
}
abstract class GasolineEngineSUV : SUV
{
}
class DieselEngineSUV_20 : DieselEngineSUV
{
public override void Go()
{
Console.WriteLine("A 2.0 diesel engine SUV is on the road");
}
public override void FuelCharging()
{
Console.WriteLine("Add diesel oil to the SUV");
}
}
class GasolineEngineSUV_16T : GasolineEngineSUV
{
public override void Go()
{
Console.WriteLine("A 1.6T gasoline engine SUV is on the road");
}
public override void FuelCharging()
{
Console.WriteLine("Add 93 gasoline to the SUV");
}
}
这就是所谓的类爆炸
Bridge模式可以让抽象部分与它的实现部分分离,上面的例子中,抽象部分指的是引擎类型,排量,小轿车或是SUV,实现部分就是车本身
class Program
{
static void Main(string[] args)
{
AbstractCar suv = new SUV();
suv.engine = new GasolineEngine();
suv.fuelCharge = new FuelCharging();
suv.Run();
}
}
abstract class AbstractCar
{
public Engine engine { get; set; }
public IFuelChargingImp fuelCharge { get; set; }
public abstract void Run();
public abstract void FuelCharging();
}
abstract class Engine
{
public string EnergyType { get; set; }
public string Volume { get; set; }
public abstract void Run();
}
class GasolineEngine : Engine
{
public override void Run()
{
}
}
class FuelCharging : IFuelChargingImp
{
public void Charge()
{
Console.WriteLine("charge with #93 oil");
}
}
class DieselCharging : IFuelChargingImp
{
public void Charge()
{
Console.WriteLine("charge with diesel oil");
}
}
interface IFuelChargingImp
{
void Charge();
}
class SUV : AbstractCar
{
public override void Run()
{
engine.Run();
}
public override void FuelCharging()
{
fuelCharge.Charge();
}
}
7. 深入思考
Bridge模式结构虽然简单,但是要用好并不容易。
1) 当一个潜在的或实际的变化发生并可能导致类的数量增加时,我们需要明确: 究竟是什么在变化? 理出这个变化的点是OO的核心,如果对这个变化的点理解的不深刻,或者是错误的定义了这个变化点,将导致一系列问题。 在复杂的场景中,“抽象部分“往往不是一个,这个时候就暗示着可能你将同时使用多个Bridge
以上面的车子为例,变化的点可以是 “引擎类型”,也可以理解为 “动力如何提供”, 如果定义变化的点是 引擎类型,那么就是一个抽象的引擎类外加实现, 如果是 “动力如何提供”,则会定义一个“动力”的接口,同时定义不同的引擎类来实现这个接口。 这个对调用者汽车来将是不同的!取决于关注点仅仅是行为还是需要更加复杂的表现。
2) 考虑Implementor的创建, 由于Implementor是抽象部分,并且可以定义为一个可读写的聚合,因此提供了IOC的能力,Implementor的创建有三种
a) Abstraction知道所有的ConcreteImplementor,通过某些参数决定要创建哪个类
b) 提供一个默认的实现,并在必要时切换
c) 将对象的创建代理给其他类,可以通过简单工厂或者抽象工厂来实现
3) Bridge模式提供的动态切换能力
由于Bridge模式产生了一个聚合,如果这个聚合是可读写的,则提供了运行时动态切换实现的能力
8. 与其他模式的关系
Bridge模式和Strategy模式的类图是一样的,Bridge模式是结构性模式,关注在于对象直接的关系,Strategy是行为型模式,关注于行为的可替换性
Bridge通常用简单工厂或抽象工厂来创建