桥接模式及其应用
2022-06-21 本文已影响0人
文景大大
一、模式介绍
桥接模式可以将抽象部分和具体实现部分进行分离,使他们都可以独立地变化。它存在的主要目的是通过组合的方式建立两个/多个维度类之间的关系,替代通过继承的方式,实现多重继承的效果。多重继承违背了单一职责原则,复用性及扩展性较差,桥接模式可以作为多重继承方案很好的替代。
需要注意,此处的抽象部分和具体实现部分并非指面向对象设计中的高层和低层的关系,而是指一个事物的两种独立变化的维度,其中抽象包含对具体实现的引用,以此来实现桥接。一般桥接模式会包含如下四种角色:
- 抽象化角色,给出基本的行为定义,并保存一个实现化角色的引用;
- 修正抽象化角色,扩展抽象化角色,改变和修正抽象化角色的定义,非必须;
- 实现化角色,给出实现化角色的行为定义;
- 具体实现化角色,给出实现化角色的具体行为;
因此,如果一个业务场景如果满足如下的特点,就可以考虑使用桥接模式来实现:
- 在抽象和具体实现之间需要增加各自的灵活性;
- 存在多个独立变化的维度,这些维度都需要独立地扩展;
- 不希望使用继承,使用继承会导致类爆炸;
我们来看一个实际的案例,去咖啡店买咖啡有多种套餐,分析下来,咖啡有两个维度:
- 大小:中杯、大杯、超大杯等;
- 添加物:牛奶、果汁、奶昔等;
很明显,这是两个独立变化的维度,我们使用桥接模式来实现咖啡的套餐。
public abstract class Coffee {
/**
* 保持对实现化角色接口的引用,实现桥接
*/
protected ICoffeeAdditives additives;
public Coffee(ICoffeeAdditives additives) {
this.additives = additives;
}
/**
* 定义公共行为
* @param num
*/
public abstract void orderCoffee(int num);
}
@Slf4j
public class RefineCoffee extends Coffee{
public RefineCoffee(ICoffeeAdditives additives) {
super(additives);
}
@Override
public void orderCoffee(int num) {
log.info("{}杯咖啡!", num);
}
/**
* 增强行为
*/
public void sayHello(){
log.info("谢谢惠顾!");
}
}
@Slf4j
public class MiddleCoffee extends RefineCoffee{
public MiddleCoffee(ICoffeeAdditives additives) {
super(additives);
}
@Override
public void orderCoffee(int num) {
log.info("中杯!");
super.orderCoffee(num);
// 桥接另外一个维度的行为
additives.addSomething();
}
}
public interface ICoffeeAdditives {
/**
* 定义实现化角色的通用行为
*/
void addSomething();
}
@Slf4j
public class MilkAdditives implements ICoffeeAdditives{
@Override
public void addSomething() {
log.info("添加牛奶!");
}
}
@Slf4j
public class JuiceAdditives implements ICoffeeAdditives{
@Override
public void addSomething() {
log.info("添加果汁!");
}
}
public class Main {
public static void main(String[] args) {
RefineCoffee coffee = new MiddleCoffee(new MilkAdditives());
coffee.orderCoffee(2);
coffee.sayHello();
}
}
如此,杯体大小和添加物两个维度无论再怎么扩展,只要增加对应的扩展类即可,无需修改已有的内容,符合开闭原则。
二、使用案例
- JDBC的Driver;
三、模式总结
3.1 优点
- 分离了抽象和具体实现两个维度;
- 提高系统的可扩展性;
- 符合开闭原则、合成复用原则;
3.2 缺点
- 需要识别出独立的变化维度;
- 增加了系统的复杂性,降低了可读性;