【设计模式】桥接模式

101 阅读6分钟

介绍

是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。

意图: 将抽象部分与实现部分分离,使它们都可以独立的变化。

主要解决: 在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

何时使用: 实现系统可能有多个角度分类,每一种角度都可能变化。

如何解决: 把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。

关键代码: 抽象类依赖实现类。

应用实例:

  1. JDK提供的JDBC数据库访问接口API正是经典的桥接模式的实现者,接口内部可以通过实现接口来扩展针对不同数据库的具体实现来进行扩展,而对外的仅仅只是一个统一的接口调用,调用方过于抽象,可以将其看做每一个JDBC调用程序(这是真实实物,当然不存在抽象)

优点:

  1. 抽象和实现的分离。
  2. 优秀的扩展能力。
  3. 实现细节对客户透明。

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

使用场景:

  1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  2. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

其实要完成桥接模式,注意点并不多,重在理解模式的使用场景。

注意点:

  1. 定义一个桥接口,使其与一方绑定,这一方的扩展全部使用实现桥接口的方式。
  2. 定义一个抽象类,来表示另一方,在这个抽象类内部要引入桥接口,而这一方的扩展全部使用继承该抽象类的方式。

其实我们可以发现桥接模式应对的场景有方向性的,桥绑定的一方都是被调用者,属于被动方,抽象方属于主动方。

注意事项: 对于两个独立变化的维度,使用桥接模式再适合不过了。

举例

假如你有一个几何 形状Shape类, 从它能扩展出两个子类: 圆形Circle和 方形Square 。 你希望对这样的类层次结构进行扩展以使其包含颜色, 所以你打算创建名为 红色Red和 蓝色Blue的形状子类。 但是, 由于你已有两个子类, 所以总共需要创建四个类才能覆盖所有组合, 例如 蓝色圆形Blue­Circle和 红色方形Red­Square 。

桥接模式解决的问题

在层次结构中新增形状和颜色将导致代码复杂程度指数增长。 例如添加三角形状, 你需要新增两个子类, 也就是每种颜色一个; 此后新增一种新颜色需要新增三个子类, 即每种形状一个。 如此以往, 情况会越来越糟糕。

问题的根本原因是我们试图在两个独立的维度——形状与颜色——上扩展形状类。 这在处理类继承时是很常见的问题。

桥接模式通过将继承改为组合的方式来解决这个问题。 具体来说, 就是抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。

桥接模式的解决方案

根据该方法, 我们可以将颜色相关的代码抽取到拥有 红色蓝色两个子类的颜色类中, 然后在 形状类中添加一个指向某一颜色对象的引用成员变量。 现在, 形状类可以将所有与颜色相关的工作委派给连入的颜色对象。 这样的引用就成为了 形状颜色之间的桥梁。 此后, 新增颜色将不再需要修改形状的类层次, 反之亦然。

类图

桥接设计模式

  1. 抽象部分 (Abstraction) 提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。

  2. 实现部分 (Implementation) 为所有具体实现声明通用接口。 抽象部分仅能通过在这里声明的方法与实现对象交互。

    抽象部分可以列出和实现部分一样的方法, 但是抽象部分通常声明一些复杂行为, 这些行为依赖于多种由实现部分声明的原语操作。

  3. 具体实现 (Concrete Implementations) 中包括特定于平台的代码。

  4. 精确抽象 (Refined Abstraction) 提供控制逻辑的变体。 与其父类一样, 它们通过通用实现接口与不同的实现进行交互。

  5. 通常情况下, 客户端 (Client) 仅关心如何与抽象部分合作。 但是, 客户端需要将抽象对象与一个实现对象连接起来。

实现

我们有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircleGreenCircleShape 是一个抽象类,将使用 DrawAPI 的对象。BridgePatternDemo 类使用 Shape 类来画出不同颜色的圆。

桥接模式的 UML 图

步骤 1

创建桥接实现接口。

    public interface DrawAPI {
       public void drawCircle(int radius, int x, int y);
    }

步骤 2

创建实现了 DrawAPI 接口的实体桥接实现类。

    public class RedCircle implements DrawAPI {
       @Override
       public void drawCircle(int radius, int x, int y) {
          System.out.println("Drawing Circle[ color: red, radius: "
             + radius +", x: " +x+", "+ y +"]");
       }
    }

    public class GreenCircle implements DrawAPI {
       @Override
       public void drawCircle(int radius, int x, int y) {
          System.out.println("Drawing Circle[ color: green, radius: "
             + radius +", x: " +x+", "+ y +"]");
       }
    }

步骤 3

使用 DrawAPI 接口创建抽象类 Shape

public abstract class Shape {
   protected DrawAPI drawAPI;
   protected Shape(DrawAPI drawAPI){
      this.drawAPI = drawAPI;
   }
   public abstract void draw();  
}
​

步骤 4

创建实现了 Shape 抽象类的实体类。

public class Circle extends Shape {
   private int x, y, radius;
 
   public Circle(int x, int y, int radius, DrawAPI drawAPI) {
      super(drawAPI);
      this.x = x;  
      this.y = y;  
      this.radius = radius;
   }
 
   public void draw() {
      drawAPI.drawCircle(radius,x,y);
   }
}

步骤 5

使用 Shape 和 DrawAPI 类画出不同颜色的圆。

public class BridgePatternDemo {
   public static void main(String[] args) {
      Shape redCircle = new Circle(100,100, 10, new RedCircle());
      Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
 
      redCircle.draw();
      greenCircle.draw();
   }
}

步骤 6

执行程序,输出结果:

Drawing Circle[ color: red, radius: 10, x: 100, 100]
Drawing Circle[  color: green, radius: 10, x: 100, 100]