👉文章示例代码👈

定义

将抽象部分与它的具体实现部分分离,使它们都可以独立地变化。

桥接模式是通过组合的方式建立两个类之间的联系,而不是通过继承。

四个角色

桥接模式中主要有以下四个角色:

  • 抽象化角色Abstraction:定义抽象类,同时包含一个对实现化对象的引用。
  • 扩展抽象化角色Refined Abstraction:抽象化角色的子类,实现父类中的方法,同时通过组合关系调用实现化角色当中的方法。
  • 实现化角色Implementor:定义实现类的接口,供扩展抽象化角色调用。
  • 具体实现化角色Concrete Implementor:定义实现化角色接口的具体实现。

场景示例

笔者这里用不同的笔(圆珠笔、铅笔)来绘制不同图形(圆形、正方形)来举例。

这里先不引进桥接模式,而是采用继承的方式来实现。

创建抽象类

这里创建一个抽象父类,主要是为了做接口约束。

/**
 * @author zhh
 * @description 笔类
 * @date 2020-02-26 14:52
 */
public abstract class Pen {

    /**
     * 笔的名称
     */
    private String name;

    public Pen(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void draw();
}

创建抽象子类

创建不同笔种的抽象子类,继承抽象父类 ​Pen 。

/**
 * @author zhh
 * @description 铅笔类
 * @date 2020-02-26 14:55
 */
public abstract class Pencil extends Pen {

    public Pencil() {
        super("铅笔");
    }
}


/**
 * @author zhh
 * @description 圆珠笔类
 * @date 2020-02-26 14:56
 */
public abstract class BallPen extends Pen {

    public BallPen() {
        super("圆珠笔类");
    }
}

创建具体的实现子类

/**
 * @author zhh
 * @description 铅笔画圆类
 * @date 2020-02-26 14:56
 */
public class CircleWithPencil extends Pencil {

    @Override
    public void draw() {
        System.out.println(getName() + "画圆");
    }
}


/**
 * @author zhh
 * @description 铅笔画正方形类
 * @date 2020-02-26 14:58
 */
public class SquareWithPencil extends Pencil {

    @Override
    public void draw() {
        System.out.println(getName() + "画正方形");
    }
}


/**
 * @author zhh
 * @description 圆珠笔画圆类
 * @date 2020-02-26 14:57
 */
public class CircleWithBallPen extends BallPen {

    @Override
    public void draw() {
        System.out.println(getName() + "画圆");
    }
}


/**
 * @author zhh
 * @description 圆珠笔画正方形类
 * @date 2020-02-26 14:59
 */
public class SquareWithBallPen extends BallPen {

    @Override
    public void draw() {
        System.out.println(getName() + "画正方形");
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-26 15:00
 */
public class Test {

    public static void main(String[] args) {
        Pen circleWithBallPen = new CircleWithBallPen();
        circleWithBallPen.draw();

        Pen circleWithPencil = new CircleWithPencil();
        circleWithPencil.draw();

        Pen squareWithBallPen = new SquareWithBallPen();
        squareWithBallPen.draw();

        Pen squareWithPencil = new SquareWithPencil();
        squareWithPencil.draw();
    }
}

测试类的输出结果如下:

圆珠笔类画圆

铅笔画圆

圆珠笔类画正方形

铅笔画正方形

类结构图

以上示例类的结构图如下所示

image.png

问题所在

上述代码虽然能满足我们的需求,但是你会发现一个问题。

当我们需要扩展一项内容的时候,比方说,用各种笔来绘制直线图形,那这个时候我们就需要在示例中的抽象子类 ​Pencil 、 ​BallPen 当中各自增加 ​LineWithPencil 类和 ​LineWithBallPen 类。又或者,我们需要使用彩笔来绘制各种图形,那我们需要新增一个抽象子类 ​ColourPen ,然后写出其具体子类 ​CircleWithColourPen 类和 ​SquareWithColourPen 类。

换句话也就是说,我们在笔和图形这两个维度中任意增加一种情况,就需要增加不同维度数量的类。

延伸到多个维度,这种方式会导致类的成倍的增加,发生类爆炸。同时这种设计也使得程序的代码变得相当的臃肿。可见继承在这种场景中是相当麻烦的。

方案改进

现在引入桥接模式对上述示例进行实现。

通过示例,我们可以简单分析得知,这里变化的主要有两个部分,一个部分是笔的种类,另一个部分则是图形的种类。通过桥接模式,我们把这种多个维度分离出来,让其独立的变化,减少之间的耦合。

创建实现化角色

/**
 * @author zhh
 * @description 图形类
 * @date 2020-02-26 14:57
 */
public interface Shape {

    String draw();
}

创建具体实现化角色

/**
 * @author zhh
 * @description 圆形类
 * @date 2020-02-26 14:57
 */
public class Circle implements Shape {

    public String draw() {
        return "画圆";
    }
}


/**
 * @author zhh
 * @description 正方形类
 * @date 2020-02-26 14:57
 */
public class Square implements Shape {

    public String draw() {
        return "画正方形";
    }
}

创建抽象化角色

此处抽象化角色通过组合的方式,包含了一个对实现化角色的引用。

/**
 * @author zhh
 * @description 笔类
 * @date 2020-02-26 14:52
 */
public abstract class Pen {

    /**
     * 图形
     */
    protected Shape shape;

    public Pen(Shape shape) {
        this.shape = shape;
    }

    public abstract void draw();
}

创建扩展抽象化角色

/**
 * @author zhh
 * @description 铅笔类
 * @date 2020-02-26 14:55
 */
public class Pencil extends Pen {

    public Pencil(Shape shape) {
        super(shape);
    }

    @Override
    public void draw() {
        System.out.println("铅笔" + super.shape.draw());
    }
}


/**
 * @author zhh
 * @description 圆珠笔类
 * @date 2020-02-26 14:56
 */
public class BallPen extends Pen {

    public BallPen(Shape shape) {
        super(shape);
    }

    @Override
    public void draw() {
        System.out.println("圆珠笔" + super.shape.draw());
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-26 15:00
 */
public class Test {

    public static void main(String[] args) {
        Pen ballPen1 = new BallPen(new Circle());
        ballPen1.draw();

        Pen ballPen2 = new BallPen(new Square());
        ballPen2.draw();

        Pencil pencil1 = new Pencil(new Circle());
        pencil1.draw();

        Pencil pencil2 = new Pencil(new Square());
        pencil2.draw();
    }
}

测试类的输出结果如下:

圆珠笔画圆

圆珠笔画正方形

铅笔画圆

铅笔画正方形

类结构图

以上示例类的结构图如下所示

image.png

通过类图我们可以清楚地看到实现化角色 ​Shape 和抽象化角色 ​Pen 之间是一比一的组合关系,两者都可以独立的扩展自己。如论两者如何进行组合,都不会发生类爆炸的情况。

总结

适用场景

  • 在抽象部分和具体实现部分之间需要增加更多的灵活性。
  • 一个类存在两个(或者多个)独立变化的维度,并且这两个(或者多个)维度都需要独立地进行扩展。
  • 由于多层继承导致系统类的个数剧增,或者说不想使用继承。

优点

  • 抽象部分和具体实现部分分离
  • 提高了系统的可扩展性
  • 避免类爆炸

缺点

  • 增加了系统的理解与设计难度
  • 使用范围具有局限性,需要识别出系统中两个独立变化的维度

参考