搞懂Java桥接模式,打破继承局限性,轻松实现多维变化
来源:mikechen的互联网架构
晦涩难懂的桥接模式,这篇文章一次就说清楚了。
了解并掌握桥接模式,对理解面向对象设计有非常大的帮助,值得深入学习。
前面,我们介绍了:
7 大设计原则(41张图解、2万多字)
5 大创建型设计模式总结,20 张图彻底掌握
今天我们主要介绍桥接模式,本文是设计模式系列第15篇。
桥接模式(Bridge)是一种结构性模式,指将抽象与实现分离,使它们可以独立变化。
概念有点晦涩难懂,我们用例子来帮助理解。
假设:一个水果类 fruit ,它可以扩展出两个子类:香蕉 banana 、苹果 apple。我们需要扩展类层次结构使其包含颜色,因此创建了名为红色 Red 和黄色 Yellow 的颜色子类。
由于已经有两个子类, 现在需要创建四个类才能覆盖所有组合,例如:黄色苹果 YellowApple、红色香蕉 RedBanana。
在这样的层次结构中,新增形状和颜色将导致代码复杂程度指数增长。
我们每新增一种水果,就需要新增两个子类(两种颜色),每新增一种新的颜色,就需要新增三个子类, 即每种水果一个。
以此类推,所有组合类的数量将以几何级数增长,情况会越来越糟糕。
问题的根本原因在于:我们是在两个独立的维度(水果与颜色)上进行扩展,这在处理继承时是很常见的问题。
桥接模式将继承改为组合的方式,非常完美的解决了这个问题。
我们抽取其中一个维度,让它成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。
将颜色相关的代码抽取到拥有红色、黄色两个子类的颜色类中, 然后在水果类中添加一个指向某一颜色对象的引用成员变量。
水果类就可以将所有与颜色相关的工作委派给连入的颜色对象,这样的引用就成为了 水果 和 颜色 之间的桥梁。
之后,我们再新增颜色,就不用修改水果的类层次了。
02桥接模式的结构桥接模式的 UML 类图:
桥接模式的主要构成:
抽象化(Abstraction):定义抽象类,并包含一个对实现化(Implementor)的引用,Abstraction 充当桥接类;
扩展抽象化(Refined Abstraction):是抽象化(Abstraction)的子类,实现父类中的业务方法,并通过组合关系调用实现化(Implementor)中的业务方法;
实现化(Implementor):定义实现化角色的接口,供扩展抽象化(Refined Abstraction)调用;
具体实现化(Concrete Implementor):给出实现化(Implementor)接口的具体实现。
03桥接模式的实现我们通过一个实例,来看看桥接模式是如何实现的。
这里仍然以水果与颜色为例,用这两个独立的维度,来实现给不同的水果添加上不同的色彩。
1)ColorAPI :用于添加颜色的接口
public interface ColorAPI { public void paint();}2)YellowColorAPI :添加黄色的实现类
public class YellowColorAPI implements ColorAPI { @Override public void paint() { System.out.println("添加黄色"); }}3)RedColorAPI :添加红色的实现类
public class RedColorAPI implements ColorAPI{ @Override public void paint() { System.out.println("添加红色"); }}4)Fruit :抽象水果类
public abstract class Fruit{ protected ColorAPI colorAPI; //添加一个颜色的成员变量以调用 ColorAPI 的方法来实现给不同的水果添加颜色 public void setDrawAPI(ColorAPI colorAPI) { //注入颜色成员变量 this.colorAPI= colorAPI; } public abstract void draw(); }5)Apple :苹果类
public class Circle extends Fruit{ @Override public void draw() { System.out.print("我是苹果"); colorAPI.paint(); }}6)Banana :香蕉类
public class Rectangle extends Fruit{ @Override public void draw() { System.out.print("我是香蕉"); colorAPI.paint(); }}7)Client:客户端
public class Client { public static void main(String[] args) { //创建一个苹果 Fruit fruit= new Apple(); //给苹果红色的颜料 fruit.setDrawAPI(new RedColorAPI()); //上色 fruit.draw(); //创建一个香蕉 Fruit fruit1 = new Banana(); //给香蕉黄色的颜料 fruit1.setDrawAPI(new YellowColorAPI()); //上色 fruit1.draw(); }}8)结果
我是苹果画上红色我是香蕉画上黄色9)新增一种水果
如果要新增一种水果桔子,不必将每一种颜色都增加一个,只需要新增一个水果类,在客户端调用时,按照需求来挑选即可。
例如,新增桔子:
public class Triangle extends Fruit{ @Override public void draw() { System.out.println("我是桔子"); colorAPI.paint(); }}10)新增一种颜色
如果要新增一种颜色,不用去更改类的层次,只需要增加一个新的颜色,实现 ColorAPI 的接口就行了。
例如,新增一个颜色黑色:
public class BlackColorAPI implements ColorAPI { @Override public void paint() { System.out.println("画上黑色"); }}实现系统可能有多个角度分类(例如例子中的形状与颜色),每一种分类都有可能变化。
桥接模式用组合关系代替继承关系来实现,将这种多角度分离出来让他们独立变化,降低了抽象和实现这两个可变维度的耦合度。
04桥接模式的使用场景桥接模式几种常见的使用场景:
系统中某个类存在多个实现,但是这些实现不应该对客户端产生影响;
需要在系统中进行抽象化和实现化之间的解耦;
需要在开发过程中灵活地切换和组合不同的抽象类和实现类。
05桥接模式的优缺点桥接模式的优点:
抽象与实现分离,扩展能力强;
符合开闭原则、合成复用原则;
实现细节对客户透明。
桥接模式的缺点:
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解及设计难度。
总结通过本文,我们了解并掌握桥接模式。桥接模式将抽象与行为实现分离开来,保持了各部分的独立性以及应对他们的功能扩展。
在实际项目中运用桥接模式时,值得注意以下 3 点:
在设计类时,尽量遵循桥接模式的原则,尤其是对于那些可能变化的部分,需要将它们抽象出来;
使用桥接模式时,需要确保抽象类和实现类之间的接口定义足够清晰,以便于后续的扩展和维护;
在具体实现中,我们需要采用工厂模式以及依赖注入等方式来创建和注入抽象和具体实现类。
扫一扫,关注我们