概念:
先把所谓的官方定义拿出来瞅瞅:
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
在桥接模式结构图中包含如下几个角色:
●Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
●RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
●Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。
●ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。
筒子们,看懂的请举手,估计寥寥无几,反正我学习该篇的时候是越看越闷的,完全不懂说的是什么,也可能是我道行太浅,水太深。
分析:下面我们就慢慢剖析那一堆难以理解的术语,看看到底什么是桥接模式,到底用在哪,怎么用,有什么好处。
1.首先我们来理解一下桥接模式的适用场景,网上有个大神举的例子很好:
有两种毛笔和蜡笔。假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3×12 = 36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便。
简单的总结就是对于画笔这个产品来说,把他相关的画笔的粗细和画笔颜色这两个不相关的维度进行解耦,因为本身这两个维度就没有什么相关性,是可以解耦的。算是基本找出了使用的场景。那对于画笔这个产品好理解,就是把笔头和颜色进行分解组装嘛,一支笔可以套上不同粗细的笔头,然后再染上不同颜色的颜料,但代码怎么去实现呢,这就是桥接模式要做的事了。
示例:
就用上面这个画笔的示例吧:
按照傻大粗的方式来处理这个问题(以前也这么干过)。不是要不同的画笔吗,那就定义一堆画笔,大号红色的,大号绿色的…小号红色的…
public class BigRedPaint {
public void paint(){
System.out.print("大号红色画笔");
}
}
public class BigBluePaint {
public void paint(){
System.out.print("大号绿色画笔");
}
}
...
public class smallBluePaint {
public void paint(){
System.out.print("小号绿色画笔");
}
}
建了一大堆的画笔,貌似搞定了,但是现在来一个新的型号的需求,例如12号画笔,那就再定一大堆不同颜色的12号画笔。凉凉了~。
现在需要解决这个傻大粗的问题,首先想到的是那就把粗细和颜色分开,单独定义,抽象出来对应的接口。
public interface SizeInterface { // 尺寸接口
void size();
}
public interface ColorInterface { // 颜色接口
void color();
}
然后再创建3种尺寸和和12种颜色
public class BulePaint implements ColorInterface {
@Override
public void color() {
System.out.print("蓝色画笔");
}
}
...
public class BigPaint implements SizeInterface{
@Override
public void size() {
System.out.print("大号画笔");
}
}
...
然后就是什么工厂模式给他们一个个builder起来,貌似也是可以的,但是这样的话就不太能体现一个画笔这个产品的整体性。
好吧,我们现在换个方式来处理这个问题。
(1)首先把SizeInterface 这个接口变换一下,写成一个抽象类。
这个就是上面定义种说的 Abstraction(抽象类):其中定义了一个Implementor(实现类接口)也就是ColorInterface , 他可以操作这个接口对象,可以使用不同的颜色实体。
public abstract class SizeAbstractionClass {
protected ColorInterface colorInterface;
public void setColorInterface(ColorInterface colorInterface) { // 传进来一个颜色的实体
this.colorInterface = colorInterface;
}
public abstract void size();
}
(2)那么ColorInterface 这个接口就是 定义中说的 Implementor(实现类接口)
public interface ColorInterface { // 颜色接口
void color();
}
(3)具体的实现类还是要的,不同颜色的画笔类。相当于定义中说的ConcreteImplementor(具体实现类)
public class BulePaint implements ColorInterface {
@Override
public void color() {
System.out.print("蓝色画笔");
}
}
public class RedPaint implements ColorInterface {
@Override
public void color() {
System.out.print("红色画笔");
}
}
...
(4)不同尺寸的画笔,继承上面的 SizeAbstractionClass 相当于定义中说的 RefinedAbstraction(扩充抽象类) 它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
public class BigPaint extends SizeAbstractionClass {
@Override
public void size() { // 实现了在Abstraction中声明的抽象业务方法
System.out.print("大号画笔");
}
public void userColor() { // 调用Implementor中定义的业务方法
colorInterface.color();
}
}
现在整个结构都搭建完了,供用户使用不同的尺寸和不同颜色的画笔进行绘制了。
ColorInterface bulePaint = new BulePaint(); // 实例化不同颜色的画笔
BigPaint bigPaint = new BigPaint(); // 实例化不同尺寸的画笔
bigPaint.setColorInterface(bulePaint); // 将颜色和尺寸进行统一组合
bigPaint.size(); // 调用尺寸
bigPaint.userColor(); // 调用颜色
注意: 可能有些厉害的同学就会想说那这个是不是可以用之前的适配器模式,也是把不同的两个东西联系起来。其实这还是有区别的,适配器模式做的是把两个不能通用的东西,经过转换把他们连接起来,其实是一个偷天换日的过程。而桥接模式是把一个产品的两个没有实际耦合(就是非你不可)的配件进行分解,减少他们的联系。所以一种是通过转换建立联系,一个是通过中间商减少不必要的联系。其实这么一想他们更像是一个方向的思维。
总结:
实际使用中注意以下点:
以上是参考各路大神以及加上自己经验思考的学习总结,如有错误,欢迎指正,共勉!