桥接模式是结构型模式,也是一个看定义什么的,看起来比较蒙的一个模式。
首先桥接模式有一个:
“Decouple an abstraction from its implementation so that the two can vary independently。”翻译成中文就是:“将抽象和实现解耦,让它们可以独立变化。”
这听起来好像懂了又不完全懂,咱们平时常见的桥接模式最佳实践:数据库连接池,还有一个sf4j日志。
数据库连接池,很常见,不同的数据库厂家的数据库连接信息是不同的,数据库连接池是一个具体的,但是对应的连接方法有很多,采用哪种连接池肯定不是简单的if else,实际上就是连接方法的抽象,各家连接方法发的实现。
sf4j日志的例子就更有意思,这是我看一个专栏,一个回复提到的,可以说桥接模式和sf4j说的非常明白:
多个纬度独立变化那个解释倒是比较容易理解。文中举的警报的例子很贴切。紧急程度和警报的方式可以是两个不同的纬度。可以有不同的组合方式。这与slf4j这一日志门面的设计有异曲同工之妙。slf4j其中有三个核心概念,logger,appender和encoder。分别指这个日志记录器负责哪个类的日志,日志打印到哪里以及日志打印的格式。三个纬度上可以有不同的实现,使用者可以在每一纬度上定义多个实现,配置文件中将各个纬度的某一个实现组合在一起就ok了。
一句话就是,桥接就是面向接口编程的集大成者。面向接口编程只是说在系统的某一个功能上将接口和实现解藕,而桥接是详细的分析系统功能,将各个独立的纬度都抽象出来,使用时按需组合
我这边的最简单的理解
一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。通过组合关系来替代继承关系,避免继承层次的指数级爆炸。
比如汽车具有多个维度:电动车燃油车,SUV还是MPV,货车还是客车等等,这要一个一个匹配222.。。。,真的会爆炸,干脆通过组合的方式来解耦。
桥接模式的组成:
上面都是一些形而上的,我们再看看桥接方式的组成,以下是我找到的一个UML图
抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。
再来个图帮助理解
下面我们知道桥接的组成可以通过代码来,一下我的gitlab的的demo演示
我的桥接代码演示地址
ps:我的例子有点问题,就是燃油车可以和suv这些组合子在一起,但是我这个构造函数是单独的,而不是组合方式直接添加进去,这个又要优化的,可以通过建造者builder方式来添加对应的属性。
抽象化(Abstraction)角色:Car汽车
package com.example.test.brige;
/**
* @Author: zhangpeng
* @Description:
* @Date: 2022/6/9
*/
public abstract class Car {
protected Ienergy ienergy;
protected Itype itype;
public abstract void call();
}
修正抽象化(RefinedAbstraction)角色:宝马汽车 比亚迪汽车
package com.example.test.brige;
/**
* @Author: zhangpeng
* @Description:
* @Date: 2022/6/9
*/
public class BMWCar extends Car{
public BMWCar(Ienergy ienergy){
call();
ienergy.energyType();
}
public BMWCar(Itype itype){
call();
itype.carType();
}
@Override
public void call() {
System.out.println("BMW");
}
}
实现化(Implementor)角色:汽车的一些属性,比如是不是新能源还是燃油车,什么车型SUV还是MPV
(实际这些都在Car这个类里呢,毕竟是Car的属性)
protected Ienergy ienergy;
protected Itype itype;
具体实现化(ConcreteImplementor)角色:汽车的一些属性具体情况
package com.example.test.brige;
/**
* @Author: zhangpeng
* @Description:
* @Date: 2022/6/9
*/
public class GasEnergy implements Ienergy{
@Override
public void energyType() {
System.out.println("-------烧汽油的--------");
}
}
桥接模式的优缺点:
优点:
- 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
- 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
- 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。
缺点:
- 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累
。