我们从软件角度来分析,一个系统由于自身的逻辑,会有两个或多个维度的变化,应对这种变化,我们可以使用桥接模式来进行系统的解耦。
模式定义与特点
- 定义:将抽象与实现分离,使它们可以独立变化,它是一种对象结构型模式。
桥接模式可以将一个系统的抽象部分和实现部分分离,使它们都可以独立地进行变化。对应到上述例子中就是:电脑的型号可以自由变化,电脑内置的操作系统也可以自由变化,这样不同的维度能够组合出多种实际的结果。
参与角色
- 抽象化角色(Abstraction):定义抽象类,并包含一个对
实现化对象
的引用。 - 扩展抽象化角色(Refine Abstraction):是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,不给出具体的实现,供扩展抽象化角色调用
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现
- 抽象化角色(Abstraction):定义抽象类,并包含一个对
- 类结构图
抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身。
结构代码示例
实现化角色
abstract class Implementor { /** * 抽象方法,实现抽象部分需要的某些功能 */ public abstract void operation1(); }
具体实现化角色
class ConcreteImplementorA extends Implementor { @Override public void operationImpl() { //具体操作 } } class ConcreteImplementorB extends Implementor { @Override public void operationImpl() { //具体操作 } }
抽象化角色类
它声明了一个方法
operation()
,并给出了它的实现。这个实现是通过向实现化对象的委派(也就是调用实现化对象的operationImpl()
方法)实现的。abstract class Abstraction { protected Implementor implementor; public Abstraction(Implementor implementor) { this.implementor = implementor; } /** * 示例方法 */ public void operation() { implementor.operationImpl(); } }
扩展抽象化角色
class RefinedAbstraction extends Abstraction { public RefinedAbstraction(Implementor implementor) { super(implementor); } /** * 其他的操作方法 */ public void otherOperation() { //对 Abstraction 中的 operation 方法进行扩展 } }
一般而言,实现化角色中的每个方法都应当有一个抽象化角色中的某一个方法与之对应,但是反过来则不一定。换而言之,抽象化角色的接口比实现化角色的接口宽。抽象化角色除了提供与实现化角色相关的方法之外,还有可能提供其他的方法;而实现化角色则往往仅为实现抽象化角色的相关行为而存在。
客户端调用
public class BridgeTest{ public static void main(String[] args) { Implementor impl = new ConcreteImplementorA(); Abstraction abs = new RefinedAbstraction(impl); abs.operation(); } }
案例分析
桥接(Bridge)模式模拟女士皮包的选购。
女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化,所以采用桥接模式来实现女士皮包的选购比较合适。
本实例按用途分可选钱包(Wallet)和挎包(HandBag),按颜色分可选黄色(Yellow)和红色(Red)。可以按两个维度定义为颜色类和包类。
颜色类(Color)是一个维度,定义为实现化角色,它有两个具体实现化角色:黄色和红色,通过 getColor() 方法可以选择颜色;包类(Bag)是另一个维度,定义为抽象化角色,它有两个扩展抽象化角色:挎包和钱包,它包含了颜色类对象,通过 getName() 方法可以选择相关颜色的挎包和钱包。
实现化角色
interface Color { abstract String getColor() ; }
具体实现化角色
//具体实现话角色1 class Yello implements Color{ @Override public String getColor() { return "黄色"; } } //具体实现话角色2 class Red implements Color{ @Override public String getColor() { return "红色"; } }
抽象化角色
abstract class Bag { private Color color; public void setColor(Color color) { this.color = color; } abstract String getName() ; }
扩展抽象化角色
//扩展抽象化角色1 class HandBag extends Bag{ @Override String getName() { return color.getColor() + "HandBag"; } } //扩展抽象化角色2 class Wallet extends Bag{ @Override String getName() { return color.getColor() + "Wallet"; } }
调用客户端
public class Client { public static void main(String[] args) { //实例化一个手提包 Bag bag = new HandBag(); //设置颜色为黄色 bag.setColor(new Yello()); //获取:手提包+黄色的组装 String yelloHandBag = bag.getName(); System.out.println(yelloHandBag); System.out.println("------------"); bag.setColor(new Red()); String redHandBag = bag.getName(); System.out.println(redHandBag); } }
这样我们将颜色和包都抽离出来后,可自由进行组合桥接,在软件开发中如果一个类或一个系统有多个变化维度时,都可以尝试使用桥接模式对其进行设计。桥接模式为多维度变化的系统提供了一套完整的解决方案,并且降低了系统的复杂度。
总结
优点
- 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象
- 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数
- 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”
缺点
- 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。
适用场景
- 如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。
- “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
- 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。