桥接模式

概述

《设计模式》中对于 “桥接模式” 的动机描述如下:

将抽象部分与它的实现部分分离,使他们可以独立地变化

桥接模式的类结构图一般如下所示:

Bridge.png

一般在以下几个场景使用桥接模式:

  • 不希望抽象和它的实现之间有一个固定的绑定关系
  • 类的抽象以及它的实现可以通过生成子类的方式加以扩充
  • 对一个抽象的实现部分的修改应对客户端不会产生影响
  • 希望在多个对象之间共享实现,但同时需要对客户端隐藏这一点

具体实例

现在需要对一期的利润数据进行处理,对于不同的公司来讲,计算的规则可能有所不同。假设我们现在需要计算和项目相关的利润数据,定义对应的计算抽象接口如下:

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() {
        // 具体业务逻辑
    }
}

此时对于 TinaProjectProfitMikeProjectProfit 来讲,只需要实现 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] 《设计模式—可复用面向对象基础》

posted @ 2023-01-08 21:03  FatalFlower  阅读(21)  评论(0编辑  收藏  举报