👉文章示例代码👈

定义

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

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

四个角色

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

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

场景示例

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

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

创建抽象类

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @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("圆珠笔类");
}
}

创建具体的实现子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* @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() + "画正方形");
}
}

测试类及输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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

问题所在

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

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

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

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

方案改进

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

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

创建实现化角色

1
2
3
4
5
6
7
8
9
/**
* @author zhh
* @description 图形类
* @date 2020-02-26 14:57
*/
public interface Shape {

String draw();
}

创建具体实现化角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @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 "画正方形";
}
}

创建抽象化角色

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @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();
}

创建扩展抽象化角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @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());
}
}

测试类及输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @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 之间是一比一的组合关系,两者都可以独立的扩展自己。如论两者如何进行组合,都不会发生类爆炸的情况。

总结

适用场景

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

优点

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

缺点

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

参考