附链
你也可以在这些平台阅读本文:
定义
将抽象部分与它的具体实现部分分离,使它们都可以独立地变化。
桥接模式是通过组合的方式建立两个类之间的联系,而不是通过继承。
四个角色
桥接模式中主要有以下四个角色:
- 抽象化角色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();
}
}
测试类的输出结果如下:
圆珠笔类画圆
铅笔画圆
圆珠笔类画正方形
铅笔画正方形
类结构图
以上示例类的结构图如下所示
问题所在
上述代码虽然能满足我们的需求,但是你会发现一个问题。
当我们需要扩展一项内容的时候,比方说,用各种笔来绘制直线图形,那这个时候我们就需要在示例中的抽象子类 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();
}
}
测试类的输出结果如下:
圆珠笔画圆
圆珠笔画正方形
铅笔画圆
铅笔画正方形
类结构图
以上示例类的结构图如下所示
通过类图我们可以清楚地看到实现化角色 Shape
和抽象化角色 Pen
之间是一比一的组合关系,两者都可以独立的扩展自己。如论两者如何进行组合,都不会发生类爆炸的情况。
总结
适用场景
- 在抽象部分和具体实现部分之间需要增加更多的灵活性。
- 一个类存在两个(或者多个)独立变化的维度,并且这两个(或者多个)维度都需要独立地进行扩展。
- 由于多层继承导致系统类的个数剧增,或者说不想使用继承。
优点
- 抽象部分和具体实现部分分离
- 提高了系统的可扩展性
- 避免类爆炸
缺点
- 增加了系统的理解与设计难度
- 使用范围具有局限性,需要识别出系统中两个独立变化的维度