桥接模式就是将抽象部分与实现部分分离,使它们分别独立,然后搭建桥梁将它们以组合而不是继承的关系联系起来。其实在学习桥接模式的时候,大可以将抽象跟实现这个概念先去掉,先这样理解:
桥接模式就是首先分开两部分,然后搭建桥梁将两个部分联系起来。
没有自己画图,借用廖雪峰网站的图记录一下我对桥接模式的理解
结构图
先来个结构图
- Abstraction:定义抽象类的接口,在构造函数中传入Implementor作为参数。
- RefinedAbstraction:扩充 Abstraction。
- Implementor:定义实现类的接口。
- ConcreteImplementor:实现 Implementor 接口并定义它的具体实现。
抽象部分就是 Abstraction,实现部分就是 Implementor,在这个结构图中,它们是互相独立的。
桥接模式,就是搭建桥梁的意思,只要理解了桥梁在哪就懂了大半桥接模式了。桥梁就是Implementor,在Abstraction构造函数中传入Implementor作为参数就是搭建桥梁,传入Implementor以后在Abstraction可以使用Implementor调用Implementor的方法。
作用
模式服务于业务,先说说为什么会有桥接模式,也就是它是用在什么地方的。
它用在一个对象有两个不同维度的时候,例如买一杯奶茶,它的维度有1.大杯中杯;2.多糖少糖;一辆汽车,它有1.品牌;2.引擎。
桥接模式将抽象部分与实现部分分离
例子
下面就用廖雪峰网站的图做例子更好的了解桥接模式。
假设某个汽车厂商生产三种品牌的汽车:Big、Tiny和Boss,每种品牌又可以选择燃油、纯电和混合动力。
传统模式
如果用传统的继承来表示各个最终车型,一共有3个抽象类加9个最终子类:
┌───────┐
│ Car │
└───────┘
▲
┌──────────────────┼───────────────────┐
│ │ │
┌───────┐ ┌───────┐ ┌───────┐
│BigCar │ │TinyCar│ │BossCar│
└───────┘ └───────┘ └───────┘
▲ ▲ ▲
│ │ │
│ ┌───────────────┐│ ┌───────────────┐│ ┌───────────────┐
├─│ BigFuelCar │├─│ TinyFuelCar │├─│ BossFuelCar │
│ └───────────────┘│ └───────────────┘│ └───────────────┘
│ ┌───────────────┐│ ┌───────────────┐│ ┌───────────────┐
├─│BigElectricCar │├─│TinyElectricCar│├─│BossElectricCar│
│ └───────────────┘│ └───────────────┘│ └───────────────┘
│ ┌───────────────┐│ ┌───────────────┐│ ┌───────────────┐
└─│ BigHybridCar │└─│ TinyHybridCar │└─│ BossHybridCar │
└───────────────┘ └───────────────┘ └───────────────┘
如果要新增一个品牌,或者加一个新的引擎(比如核动力),就得在三个品牌下面都添加一个核动力引擎的类,三个品牌就要添加三个类,100个品牌添加100个类,这就是传说中的子类爆炸。
如果用桥接模式呢
桥接模式
引用我前面那句话:
桥接模式就是首先分开两部分,然后搭建桥梁将两个部分联系起来。
所以第一步就是将两个部分分开。上面的汽车有两个维度,1.品牌;2.引擎。两个维度非常适合桥接模式。随便分一个到抽象那部分,一个到实现那部分。
第二步搭建桥梁。上面也说了1.桥梁模式是组合而不是继承的;2.桥梁就是Implementor,在Abstraction构造函数中传入Implementor作为参数就是搭建桥梁,传入Implementor以后在Abstraction可以使用Implementor调用Implementor的方法。
所以,搭建桥梁就是在抽象部分的父类的构造函数传入另一个部分的父类作为参数。
桥接模式图如下:
┌───────────┐
│ Car │
└───────────┘
▲
│
┌───────────┐ ┌─────────┐
│RefinedCar │ ─ ─ ─>│ Engine │
└───────────┘ └─────────┘
▲ ▲
┌────────┼────────┐ │ ┌──────────────┐
│ │ │ ├─│ FuelEngine │
┌───────┐┌───────┐┌───────┐ │ └──────────────┘
│BigCar ││TinyCar││BossCar│ │ ┌──────────────┐
└───────┘└───────┘└───────┘ ├─│ElectricEngine│
│ └──────────────┘
│ ┌──────────────┐
└─│ HybridEngine │
└──────────────┘
左边是品牌,也就是抽象部分,右边是引擎,实现部分。 这时候添加核动力的引擎只需要添加一个类继承Engine就行。
调用的时候使用RefinedAbstraction(RefinedCar),例如你要创建品牌为Boss,引擎为Hybrid的汽车,只需要这样:
RefinedCar car = new BossCar(new HybridEngine());
要创建品牌为Big,引擎为Electric的汽车,只需要这样 :
RefinedCar car = new BigCar(new ElectricEngine());
到这里我忽然有一个问题,如果汽车有三个维度呢,例如外壳颜色,红橙黄绿,这时候还能用桥接模式吗?如果可以要怎么修改?如果不可以那要用什么模式更有扩展性以及可维护性?
附:廖雪峰网站的代码
首先定义抽象类Car
,它引用一个Engine
:
public abstract class Car {
// 引用Engine:
protected Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public abstract void drive();
}
Engine
的定义如下:
public interface Engine {
void start();
}
紧接着,在一个“修正”的抽象类RefinedCar
中定义一些额外操作:
public abstract class RefinedCar extends Car {
public RefinedCar(Engine engine) {
super(engine);
}
public void drive() {
this.engine.start();
System.out.println("Drive " + getBrand() + " car...");
}
public abstract String getBrand();
}
这样一来,最终的不同品牌继承自RefinedCar
,例如BossCar
:
public class BossCar extends RefinedCar {
public BossCar(Engine engine) {
super(engine);
}
public String getBrand() {
return "Boss";
}
}
而针对每一种引擎,继承自Engine
,例如HybridEngine
:
public class HybridEngine implements Engine {
public void start() {
System.out.println("Start Hybrid Engine...");
}
}
客户端通过自己选择一个品牌,再配合一种引擎,得到最终的Car:
RefinedCar car = new BossCar(new HybridEngine());
car.drive();