1.什么是桥接模式
- 桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interfce)模式
- 上图表示了一个多继承的关系图,各种类型的电脑继承了电脑类,各个厂商又根据不同类型的电脑实现类自己品牌下该电脑产品,但是上面这样的继承模式扩展的时候是很麻烦的
- 比如:现在华为进军电脑市场,我们就需要分别在上图中的台式、笔记本和平板下面写3个华为品牌子类,只要是增加一个品牌就要新增3个子类
- 从另一个角度来看,如果电脑的实现类多了一个,比如量子计算机,那么我们需要将所有厂商对于它的实现类都写出来
- 导致上面扩展如此麻烦的原因是什么?是上面的继承关系违背了OOP 7大原则中的单一职责原则;因为上面的每一个电脑子类都同时实现了两个功能,电脑品牌+电脑类型;如果我们将类的粒度进一步切分,使得品牌和电脑类型两个类平行,扩展的事情就能够变得简单起来
- 分析:上图场景有两个变化的维度 —— 电脑品牌+电脑的类型
2.代码实现
- 就通过桥接模式完成上面的改造需求,将电脑类型和电脑品牌分离成两个接口
- 首先我们应该抽象出品牌接口,具体的厂商就去首先这个接口即可
public interface Brand { //厂商品牌接口 void info();//打印厂商名称 } public class Apple implements Brand{ //苹果品牌 @Override public void info() { System.out.print("苹果"); } } public class Lenovo implements Brand{ //联想品牌 @Override public void info() { System.out.print("联想"); } }
- 编写一个电脑类型类,我们的电脑类型类应该和品牌产生关系(否则我们如何将品牌和电脑类型联系起来?这里产生联系使用推荐的组合,而不使用继承),所以我们不应该将电脑类型类设计为接口,可以将其设计为抽象类,这样这个类就不能直接使用,必须被继承之后才能使用;
- 这个类的作用除了定义电脑实现类的约束,还实现了将厂商类组合进来,并被电脑类型类继承的作用,所以在new电脑类型类的时候,我们就需要指定这个类是哪一个厂商的这个类型的电脑
public abstract class Computer { //抽象的电脑类型类 //厂商品牌 protected Brand brand; public Computer(Brand brand) { //通过构造初始化该电脑类型类的厂商 this.brand = brand; } public void info(){ //调用厂商类的信息输出厂商/品牌的名称 brand.info(); } }
- 两种电脑类型类
class Desktop extends Computer{ //台式机 public Desktop(Brand brand) { //构造,需要传入电脑厂商 super(brand); } @Override public void info() { //输出电脑厂商+电脑类型 super.info(); System.out.println("台式机"); } } class Laptop extends Computer{ //笔记本 public Laptop(Brand brand) { //构造,需要传入电脑厂商 super(brand); } @Override public void info() { super.info(); System.out.println("笔记本"); } }
- 注意:我们只写了两个电脑类型,但是它们都继承了抽象的电脑类,所以每一个电脑类型类调用super.info()就会输出厂商品牌,然后再紧接着输出该电脑类型即可实现"品牌和电脑类型"的随意组装
- 测试
public class Test { public static void main(String[] args) { //苹果笔记本 Laptop laptop = new Laptop(new Apple()); //联想台式机 Desktop desktop = new Desktop(new Lenovo()); laptop.info(); desktop.info(); } }
- 上面的例子中,我们通过组合的方式将两个不相关的东西进行了桥接/使它们产生联系,这样做的好处在于,不管你是哪一个厂商的哪一种类型的电脑对象,在客户端使用的时候,我只需要按照我自己的要求new就可以获取到对应的产品;
- 而在原来的多继承中,我们需要将所有厂商对于所有类型的电脑的实现类都写好,在客户端使用的时候直接new,但是这样做的扩展性并不好;
- 现在我们将电脑的品牌和电脑的类型都抽象出来,并且通过"组合"的方式使得new出来的电脑类型对象自带了电脑厂商(我们需要手动传入厂商对象),这就大大减少了冗余的代码(每个厂商的每种电脑类型都去写一个是实现类),对于无论是厂商的扩展还是电脑类型的扩展都很便利,直接扩展即可,反正组合的是厂商的接口引用
3.小结
- 好处分析
- 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法,极大的减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合开闭原则,就像一座桥,可以把两个变化的维度连接起来
- 劣势分析
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
- 桥接模式要求正确识别出系统中两个独立变化的维度(如果两个东西耦合性很强,且不能拆分,就不能使用桥接模式),因此其使用范围具有一定的局限性
- 适用情况
- 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
- 常见的场景
-
Java语言通过Java虚拟机实现了平台的无关性
-
AWT中的Peer架构
-
JDBC驱动程序也是桥接模式的应用之一
-