设计模式之桥接模式(Bridge Pattern):我允许你的自我,减去的是羁绊,留下的牵挂

概念

桥接模式(Bridge Pattern)是设计模式中最复杂的模式之一,它把事物对象和该对象的具体行为、具体特征分离开来,使它们可以独自进行变化和更改。这样的解释可能比较抽象和令人费解,读者可以通过下面的应用场景进一步来理解。

应用场景

我们还以绘制图形为例,比如我们要绘制矩形Rectangle、Circle、Triangle,我们需要至少需要定义3个形状类来表示,如果我们要绘制的图形还有不同的颜色,如Red、Yello、Green等,此时我们可能有一下两种设计方案:

  1. 为每个形状类都提供颜色的实现,比如通过继承增加RedRectangle、YellowRectangle、RedCircle、YellowCircle等
  2. 根据实际需要对具体形状对象颜色属性进行组合,把颜色属性作为参数动态给到形状对象来使用,比如红色的圆形就是组合 Circle + RedAttr

如果一个有两个及以上的变化维度的系统(或对象),采用方案2可以明显减少类的维护个数,扩展起来更为方便。方案2即为桥接模式的应用,它使用组合关联关系,而不是通过继承来做扩展,这减少了类之间的耦合,减少代码的编写量。

用过Java Swing进行过图形界面开发的读者发现,界面的风格(Look and Feel)在不同操作系统下是不一样的,实际上Java为每一个GUI组件都提供了一个Peer构件,该构件在不同操作系统下有不同的实现,Swing的Peer架构就是应用了桥接模式。

在以下情况下我们可以尝试使用桥接模式:

  • 一个类存在两个独立变化的维度,且这两个维度都需要独立扩展,这样有利于开发者单独去维护维度的扩展,而不需要关注其他维度的实现
  • 继承,尤其是多层次的继承会使得类的个数和理解复杂度都急剧增加,此时桥接模式尤为适用

结构在这里插入图片描述桥接模式包含以下角色:

  • Abstraction:抽象类
  • RefinedAbstraction:扩充抽象类
  • Implementor:扩展行为或属性的接口定义(这里需要特别注意,这是Abstraction的行为或特征的实现类接口,翻译后确实词不达意)
  • ConcreteImplementor:Implementor的具体实现类

桥接模式就是将Abstraction的一些行为或属性独立的再抽象到Implementor中,在Abstraction(或RefinedAbstraction)中通过组合来动态的将Implementor的实现ConcreteImplementor组合起来,而不是用典型的继承关系来做扩展。这样就实现了一个对象(或系统)跟它的行为(或属性)能够通过不同的类单独去扩展和变化,再通过引用关系组合起来,这就是所谓的桥接。

代码示例

/** "Implementor" */
interface DrawingAPI {
    public void drawCircle(double x, double y, double radius);
}

/** "ConcreteImplementorA" 1/2 */
class DrawingAPI1 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
   }
}

/** "ConcreteImplementorB" 2/2 */
class DrawingAPI2 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius)  { 
        System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
   }
}

/** "Abstraction" */
interface Shape {
   public void draw();                             // low-level
   public void resizeByPercentage(double pct);     // high-level
}

/** "Refined Abstraction" */
class CircleShape implements Shape
{
   private double x, y, radius;
   private DrawingAPI drawingAPI;
   public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
       this.x = x;  
       this.y = y;  
     	 this.radius = radius; 
       this.drawingAPI = drawingAPI;
   }

   // low-level i.e. Implementation specific
   public void draw() {
        drawingAPI.drawCircle(x, y, radius);
   }   
   // high-level i.e. Abstraction specific
   public void resizeByPercentage(double pct) {
        radius *= pct;
   }
}

/** "Client" */
class BridgePatternDemo {
   public static void main(String[] args) {
       Shape[] shapes = new Shape[2];
       shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
       shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());

       for (Shape shape : shapes) {
           shape.resizeByPercentage(2.5);
           shape.draw();
       }
   }
}

输出:

API1.circle at 1:2 7.5
API2.circle at 5:7 27.5

优缺点

优点:

  • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。

  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

  • 实现细节对客户透明,可以对用户隐藏实现细节。

缺点:

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进

行设计与编程。

  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

作业

最后,我们给读者留一个作业:我们需要开发一个跨平台的视频播放器,在不同操作系统平台(Windows、Linux、MacOS等)上,除了可以播放多种格式的视频文件(MPEG、RMVB、AVI、WMV等),还可以播放多种格式的音频文件(WAV、MP3、AAC、OGG等),该如何进行设计呢?

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值