桥接模式
概述
《设计模式》中对于 “桥接模式” 的动机描述如下:
将抽象部分与它的实现部分分离,使他们可以独立地变化
桥接模式的类结构图一般如下所示:
一般在以下几个场景使用桥接模式:
- 不希望抽象和它的实现之间有一个固定的绑定关系
- 类的抽象以及它的实现可以通过生成子类的方式加以扩充
- 对一个抽象的实现部分的修改应对客户端不会产生影响
- 希望在多个对象之间共享实现,但同时需要对客户端隐藏这一点
具体实例
现在需要对一期的利润数据进行处理,对于不同的公司来讲,计算的规则可能有所不同。假设我们现在需要计算和项目相关的利润数据,定义对应的计算抽象接口如下:
import java.math.BigDecimal;
public abstract ProjectProfitCal {
public void operation() {
// 具体业务逻辑
}
abstract BigDecimal sumForData(List<? extends ProjData> data);
}
interface ProjData {
BigDecimal val();
}
假设现在有两家公司有不同的计算方法,现在定义两个实现类以实现利润的相关计算, Tina 公司的计算方式如下:
public class TinaProjectProfit extends ProjectProfitCal {
@Override
public BigDecimal sumForData(List<? extends ProjData> data) {
return BigDecimal.valueOf(0);
}
}
另外一家公司 Mike 的计算实现如下:
public class MikeProjectProfit extends ProjectProfitCal {
@Override
public BigDecimal sumForData(List<? extends ProjData> data) {
return BigDecimal.valueOf(1);
}
}
如果没有特殊的需求变动,那么这些代码就能够正常使用,不会有问题。但随着需求的变更,在这个项目利润计算的基础上,需要增加对项目下相关产品利润的计算,如果没有特殊的需求,直接修改 ProjectProfitCal
貌似也是可行的,但是当实现类变多的时,特别是接口与实现在不同的包中时,这种解决方式是不太适合使用的。可能想到的另一种方式是定义一个 ProjectProfitCal
的子类用于专门计算产品相关的利润,但是这同样要求定义相关的子类去实现这个接口,因此这种方式也是不太可行的
使用桥接模式,可以将具体的需求和相关的实现分离,定义相关利润计算的抽象 ProfitCal
:
public interface ProfitCal {
BigDecimal calProfit(List<? extends ProjData> data); // 计算相关数据的可获取利润
}
此时可以将 ProjectProfitCal
改为组合 ProfitCal
的模式:
public abstract ProjectProfitCal {
private final ProfitCal cal;
protected ProjectProfitCal(ProfitCal cal) {
this.cal = cal;
}
public void operation() {
// 具体业务逻辑
}
}
此时对于 TinaProjectProfit
和 MikeProjectProfit
来讲,只需要实现 ProfitCal
接口来实现与 ProjectProfitCal
的硬关联:
class TinaProjectProfit implements ProfitCal {
@Override
public BigDecimal calProfit(List<? extends ProjData> data) {
return BigDecimal.valueOf(0);
}
}
class MikeProjectProfit implements ProfitCal {
@Override
public BigDecimal calProfit(List<? extends ProjData> data) {
return BigDecimal.valueOf(1);
}
}
之后,如果有其它公司的的计算方式,同样也只需要实现 ProfitCal
,并且即使需求需要另外计算相关产品期的利润信息,只需要继承 ProjectProfitCal
扩展相关的业务功能,同时复用 ProfitCal
接口即可(实现 ProfitCal
接口以实现计算算法)
总结
桥接模式和 抽象工厂模式很像,都是通过组合的方式替换继承的方式来提高类的灵活性,尽管一般推荐使用 “组合” 来替换 “继承”,但是还是需要结合现有的上下文采取合适的处理模式
参考:
[1] 《设计模式—可复用面向对象基础》