桥接模式(Bridge Pattern)
程序员文章站
2022-07-03 14:26:23
...
模式动机:
开发一个程序,可以绘制矩形、正方形、圆形、椭圆形四种图形,且每个图形都有不同的颜色如:红、绿、蓝。
最直接的作法是:为每一种图形都提供一种颜色的类, 示意图如下:会发现完成该类图需要4*3=12个类,如果再多几种图形或是多几种颜色,会发现有非常多的子类。
现在面临的问题是子类太多,而且随着图形和颜色的增加,子类会越来越多,如果再添加一个维度:人,不同的人(如:成年男性、成年女性和小孩等)可以绘制多种不同颜色的图形,那子类将达到:3种人*4种图形*3种颜色=36个。
分析以上问题发现有图形和颜色两个维度都在变化,最终的输出结果是两个维度的组合,所以可以采用以下的方式来设计:
从类图中可以看到图形和颜色有各自的互不干扰的独立继承体系,这样的优点在于图形或是颜色遇到变化(拥抱变化设计)而扩展(对扩展开放,这里的通过继承来扩展)时可以不影响对方,且子类的数量大大减少。这种设计方式称之为桥接方式:对于有两个变化维度(也可以是多个)的系统,采用上述设计系统中的类的个数更少,系统也更宜扩展。桥接模式将继续关系转换为关联关系,从而降底了类与类之间的耦合,降底类的个数。
模式定义:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。即:不只改变你的实现,也改变你的抽象。
结合图2来理解模式的定义,“将抽象部分与它的实现部分分离”:即是将基础图形与颜色分离,基础图形是抽象部分,上色是实现部分。“使它们都可以独立地变化” :基础图形与颜色都有各自的父类,都可以*扩展。
演示代码:
设计遥控器的抽象类,不同的型号的电视有不同的实现。这种设计简单粗爆,完全没有考虑到未来的变化:遥控器需要不断改良,为不同用户设计不同的遥控器。所以这里会发生变化的有两个维度:遥控器、不同电视对遥控器的实现。
现在有了两个层次结构,一个是遥控器,一个是平台特定的电视机实现,有了桥接的存在,可以独立的改变这两个层次。
定义图形的父类及子类,作为示例,只写了矩形和正方形的子类;而且我们把图形作为桥接模式中的抽象部分,担象部分的行为是以实现部分实现的,所以图形类都有颜色类(实现部分)的引用
/** * 图形父类 * @author yuhq * */ public abstract class Shap { /** * 绘制图形 * @param color 上色器 */ public abstract void doDraw(Color color); } /** * 矩形子类 * @author yuhq * */ public class Rectangle extends Shap{ public void doDraw(Color color) { String cr = color.doColor(); System.out.println("正在绘制"+cr+"颜色的矩形"); } } /** * 正方形 * @author yuhq * */ public class Square { public void doDraw(Color color) { String cr = color.doColor(); System.out.println("正在绘制"+cr+"颜色的正方式形"); } }
/** * 颜色父类 * @author yuhq * */ public abstract class Color { public abstract String doColor(); } /** * 红色子类 * @author yuhq * */ public class Red extends Color{ public String doColor() { return "红色"; } } /** * 绿色子类 * @author yuhq * */ public class Green { public String doColor() { return "绿色"; } }
//这里的Main方法即是图3中的Client,通过该Client可以绘制各种带颜色的图形 public class DrawMain { public static void main(String[] args) { //绘制红色矩形 Shap rectangle = new Rectangle(); Color red = new Red(); rectangle.doDraw(red); //绘制绿色色矩形 Color green = new Red(); rectangle.doDraw(green); } }
输出结果:
深入思考:
桥接模式的关键是如何将变化的维度分离出来,并确定谁是抽象部分,谁是实现部分。
上述绘图的例子较为简单,而且基础图形和颜色谁是抽象部分,谁是实现部分区别不大,再举一个贴近我们生活的例子:设计一个新的接口友好的电视遥控器程序,让所有遥控器斟于相同的抽象,而对此抽象又做出许多不同的实现--每部不同型号的电视都有自己的遥控器实现。
分析:你不会第一次就做对遥控器的用户界面,事实上,我们希望随着可用性数据收集得越来越丰富的同时,持续改良遥控器。困难之处在于:遥控器(不断改良 )会改变,而电视机也会改变。你已经将用户界面抽象出来,所以可以根据不同电视机改变它的实现。事情还不只这样,随着时间 的增长,用户会对此界面提出一些想法,你还必须应对他们的反馈来改变抽象。
先看一种简单直接的设计,如下图:
设计遥控器的抽象类,不同的型号的电视有不同的实现。这种设计简单粗爆,完全没有考虑到未来的变化:遥控器需要不断改良,为不同用户设计不同的遥控器。所以这里会发生变化的有两个维度:遥控器、不同电视对遥控器的实现。
现在有了两个层次结构,一个是遥控器,一个是平台特定的电视机实现,有了桥接的存在,可以独立的改变这两个层次。
桥接的优点:
1、将实现予以解耦,让它和界面之再永久绑定。
2、抽象和实现可以独立扩展,不会影响到对方。
3、对于“具体的抽象”所做的改变,不会影响到客户。
桥接的用途和缺点:
1、适合使用在需要跨越多个平台的图形和窗口系统上。
2、当需要用不同的方式改变接口和实现时,你会发现桥接模式很好用。
3、桥接模式的缺点是增加了复杂度。