结构型——桥接模式

桥接模式概述

为什么需要桥接模式

在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。如何设计类似于 Photoshop 这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式,m 种形状和 n 种颜色的图形就有 m×n 种,不但对应的子类很多,而且扩展困难。

当然,这样的例子还有很多,如不同颜色和字体的文字、不同品牌和功率的汽车、不同性别和职业的男女、支持不同平台和不同文件格式的媒体播放器等。如果用桥接模式就能很好地解决这些问题。

桥接(Bridge)模式的优点是:

  • 抽象与实现分离,扩展能力强
  • 符合开闭原则
  • 符合合成复用原则
  • 其实现细节对客户透明

缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

桥接模式的定义

桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。 这句话比较难懂,什么是抽象?什么是实现?抽象和实现其实都可以看做是前面提到的不同维度,比如我可以把形状当做抽象,颜色当做实现。

桥接(Bridge)模式包含以下主要角色。

  1. 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  2. 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  3. 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  4. 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

其结构图如下图 所示:

桥接模式的实现

这里用一个“汽车例子”,简单实现一下桥接模式。

假设某个汽车厂商生产三种品牌的汽车:Big、Tiny和Boss,每种品牌又可以选择燃油、纯电和混合动力。如果用传统的继承来表示各个最终车型,一共有3个抽象类加9个最终子类:

如果要新增一个品牌,或者加一个新的引擎(比如核动力),那么子类的数量增长更快。所以,桥接模式就是为了避免直接继承带来的子类爆炸。使用桥接模式,可以这样实现:

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
//相当于抽象角色
public abstract class Car {
// 使用组合方式:引用Engine:
protected Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public abstract void drive();
}
//相当于抽象实现化角色
public interface Engine {
void start();
}
//相当于修正抽象角色,它可有可无,根据具体需求
public abstract class RefinedCar extends Car {
public RefinedCar(Engine engine) {
super(engine);
}
public void drive() {
this.engine.start();
System.out.println("Drive " + getBrand() + " car...");
}
public abstract String getBrand();
}
//具体抽象化角色
public class BossCar extends RefinedCar {
public BossCar(Engine engine) {
super(engine);
}

public String getBrand() {
return "Boss";
}
}
//相当于具体实现角色
public class HybridEngine implements Engine {
public void start() {
System.out.println("Start Hybrid Engine...");
}
}
public static void main(String[] args){
//创建一个Boss品牌的混动车
RefinedCar bossCar = new BossCar(new HybridEngine());
}

桥接模式的应用

应用场景

桥接模式实现比较复杂,实际应用也非常少,用于那些有多个维度变化的场景。但它提供的设计思想值得借鉴,即不要过度使用继承,而是优先拆分某些部件,使用组合的方式来扩展功能

JDK中的应用

  1. java.util.logging是JDK自带的日志包,可以将日志输出到文件、内存或者控制台,作用与我们常用的log4j类似。
    包中的Handler类和Formatter类在设计上利用了桥接模式 。

Handle和Formatter类是两个抽象类,它们可以分别独立的变化(有不同的子类);而Handle类中包含对Formatter类的引用。

  1. JDBC中的Driver接口使用了桥接模式。

    基于JDBC的应用程序,使用JDBC的API,相当于是对数据库操作的抽象的扩展,算作桥接模式的抽象部分;而具体的接口实现是由驱动来完成的,驱动这边自然就相当于桥接模式的实现部分了 。

参考资料

  1. https://www.liaoxuefeng.com/wiki/1252599548343744/1281319266943009

  2. https://zhuanlan.zhihu.com/p/58903776

  3. http://c.biancheng.net/view/1364.html

  4. https://www.icode9.com/content-4-1178561.html

  5. https://segmentfault.com/a/1190000015360433

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信