1.定义
将抽象和实现解耦,使得两者可以独立地变化
2.例子
定义看不明白?没关系,我将通过下面这个例子来阐述桥梁的概念。
小时候我们都用蜡笔画画,一盒蜡笔12种颜色。一开始我都是用最小号的蜡笔画个太阳公公、月亮婆婆足够了。后来开始画一些抽象派的作品,就得换中号的了,要不然画个背景都要描半天,好一盒中号的也是12种颜色。再后来我开始转向豪放派,中号就有些捉襟见肘了,只好换大号的了,好一盒大号的也只有12种颜色。你看,像我这样不太出名的画家就需要36种画笔,哇,太麻烦了。但是据我观察,另一些比我出名的画家倒是没有这么多笔,他们只有几把刷子和一些颜料,这样就解决了蜡笔的“种类爆炸”问题。”
==========================================================
呵呵,您是不是已经看出来了,不错,我今天要说的就是Bridge模式。为了一幅画,我们需要准备36支型号不同的蜡笔,而改用毛笔三支就够了,当然还要搭配上12种颜料。通过Bridge模式,我们把乘法运算3×12=36改为了加法运算3+12=15,这一改进可不小。那么我们这里蜡笔和毛笔到底有什么区别呢?
实际上,蜡笔和毛笔的关键一个区别就在于笔和颜色是否能够分离。【GOF95】桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。关键就在于能否脱耦。蜡笔的颜色和蜡笔本身是分不开的,所以就造成必须使用36支色彩、大小各异的蜡笔来绘制图画。而毛笔与颜料能够很好的脱耦,各自独立变化,便简化了操作。在这里,抽象层面的概念是:"毛笔用颜料作画",而在实现时,毛笔有大中小三号,颜料有红绿蓝等12种,于是便可出现3×12种组合。每个参与者(毛笔与颜料)都可以在自己的自由度上随意转换。
蜡笔由于无法将笔与颜色分离,造成笔与颜色两个自由度无法单独变化,使得只有创建36种对象才能完成任务。Bridge模式将继承关系转换为组合关系,从而降低了系统间的耦合,减少了代码编写量。
桥梁模式是一种非常简单的模式,它只是使用了类间的聚合关系、继承、重写等常用功能,但是它却提供了一个非常清晰、稳定的架构。
下面还有个小例子:
====================================================
3.桥梁模式的使用场景
- 不希望或不适合使用继承的场景:例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式
- 接口或抽象类不稳定的场景:明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败的做法
- 重用性要求比较高的场景:设计的颗粒度越细,则被重阳的可能性越大,而采用继承则受父类的限制,不可能出现太细的颗粒度
4.桥梁模式的四个角色
- Abstraction-抽象化角色(上图中的毛笔):它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。
- Implementor-实现化角色(上图中的颜料):它是接口或者抽象类,定义角色必须的行为和属性。
- RefinedAbstraction-修正抽象化角色:它引用实现化角色对抽象化角色进行修改。
- ConcreteImplementor-具体实现化角色:它实现接口或抽象类定义的方法和属性。
下面是它们的类图:
5.桥梁模式的通用代码
package _23BridgePattern; /** * 实现化角色 * (颜料) */ public interface Implementor { // 基本方法 // 画图 public void paint(); // 提名 public void title(); }
package _23BridgePattern; /** * 具体实现化角色 * (假装红颜料) */ public class ConcreteImplementor1 implements Implementor { @Override public void paint() { System.out.println("使用红色颜料画画"); } @Override public void title() { System.out.println("使用红色颜料提名"); } }
package _23BridgePattern; /** * 具体实现化角色 * (假装蓝颜料) */ public class ConcreteImplementor2 implements Implementor { @Override public void paint() { System.out.println("使用蓝色颜料画画"); } @Override public void title() { System.out.println("使用蓝色颜料提名"); } }
package _23BridgePattern; /** * 抽象化角色 * (假装毛笔) */ public abstract class Abstraction { // 定义对实现化角色的引用 private Implementor implementor; // 约束子类必须实现该构造函数 public Abstraction(Implementor implementor) { this.implementor = implementor; } // 自身的行为和属性 public void paint() { this.implementor.paint(); } public Implementor getImplementor() { return implementor; } }
package _23BridgePattern; /** * 具体抽象化角色 * (假装小号毛笔) * */ public class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor implementor) { super(implementor); } // 修正父类的行为 @Override public void paint() { super.paint(); super.getImplementor().title(); } }
package _23BridgePattern; /** * 场景类 */ public class Client { public static void main(String[] args) { // 定义一个实现化角色(颜料) // 还可以定义别的颜色 Implementor implementor = new ConcreteImplementor1(); // 定义一个抽象化角色(毛笔) // 还可以定义别的大小的毛笔 Abstraction abstraction = new RefinedAbstraction(implementor); // 执行(画画),这样你画画就不需要36支笔了,只需要三支笔,12种颜料就行了 abstraction.paint(); } }
6.桥梁模式的优点
- 抽象和实现分离:这也是桥梁模式的主要特点,它完全是为了解决继承的缺点而提出的设计模式。在该模式下,实现可以完全不受抽象的约束,不用再绑定在一个固定的抽象上。
- 优秀的扩展能力:看看我们的例子,想增加毛笔大小和颜料色彩都完全没问题,我们已经把变化的可能性减到了最小。
- 实现细节对客户透明:客户不必关心细节的实现,它已经由抽象层通过聚合完成了封装。
7.桥梁模式的注意事项
- 桥梁模式是非常简单的,使用该模式时,主要考虑如何拆分抽象和实现,并不是一涉及继承就要考虑桥梁模式,那还要继承干什么呢?桥梁模式的意图是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。
- 桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。