桥接模式¶
分类:结构型模式。
定义与理解¶
桥接模式的经典定义是:
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
图中左岸的电视对应实现层,右岸的遥控器对应抽象层;中间的桥表示抽象层持有实现对象,并通过约定的接口委托操作。两岸可以分别增加类型,再通过这座“桥”组合使用。
“将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构”是对这一定义的通俗解释。其中“在开发时分别使用”,理解为“可以分别演化和扩展”更准确;两侧仍通过约定的实现层接口协作。
“抽象”和“实现”分别指什么¶
这里的“抽象”指面向使用者提供功能的高层,“实现”指完成这些功能所需的底层能力。以本文的遥控器与电视示例为例:
| 层次 | 职责 | 可以独立变化的维度 |
|---|---|---|
| 抽象层:遥控器 | 提供开关机、换台等控制功能 | 不同遥控器类型,如 GenericRemote、SpecialRemote |
| 实现层:电视 | 实现具体设备的开关机、频道控制能力 | 不同电视品牌,如 Sony、LG |
RemoteControl 持有一个 TV 类型的对象引用,并把具体操作委托给它。这个对象组合与委托关系,就是连接两个层次结构的“桥”。 本例的 TV 使用抽象类定义实现层契约,也可以根据设计需要使用语言中的接口。
“独立变化”体现在哪里¶
- 增加一种遥控器类型时,可以利用已有的
TV操作组合新的控制功能,搭配现有品牌电视使用。 - 增加一种电视品牌时,实现
TV约定的操作,就可以与现有遥控器配合使用;本例还需要在TVFactory中增加该品牌的创建入口。 - 遥控器类型与电视品牌可以组合搭配,无需为每一种组合分别定义一个子类,例如“索尼普通遥控器”“LG 普通遥控器”“索尼特殊遥控器”等。
“独立变化”以双方约定的接口能够满足需求为前提。如果新的高层功能需要额外的底层能力,仍可能需要调整实现层契约。
判断是否使用桥接模式,关键是:是否存在两个需要独立变化的维度,并通过对象组合将它们连接起来。 代码中出现抽象类、接口及其实现类,本身还不足以说明采用了桥接模式。
类结构¶
classDiagram
direction TB
class Client
class Abstraction {
<<abstract>>
+operation()
}
class RefinedAbstraction {
+operation()
}
class Implementor {
<<interface>>
+operationImpl()
}
class ConcreteImplementorA {
+operationImpl()
}
class ConcreteImplementorB {
+operationImpl()
}
Abstraction <|-- RefinedAbstraction
Implementor <|.. ConcreteImplementorA
Implementor <|.. ConcreteImplementorB
Abstraction --> Implementor : 委托
Client ..> Abstraction : 使用
角色定义¶
| 角色 | 定义 |
|---|---|
| Client(客户端) | 使用抽象层提供的操作,无需依赖具体实现类。 |
| Abstraction(抽象层) | 定义面向客户端的操作,持有实现层接口,并将底层工作委托给实现对象。 |
| RefinedAbstraction(扩展抽象) | 继承抽象层,利用实现层接口组合或扩展高层行为,可独立于具体实现演化。 |
| Implementor(实现层接口) | 定义抽象层所需的底层操作,其接口不必与抽象层操作一一对应。 |
| ConcreteImplementorA(具体实现 A) | 实现底层操作,提供一种可与抽象层搭配的实现方式。 |
| ConcreteImplementorB(具体实现 B) | 提供另一种实现方式,与 A 处于同一实现层次,可独立于抽象层扩展。 |
Head First 示例:遥控器与不同品牌电视¶
源码中的 RemoteControl 和 TV 都是抽象类。前者提供开关机、换台与选择电视的具体方法,后者声明电视需要实现的底层操作。GenericRemote 与 SpecialRemote 组合这些操作提供不同名称的换台按键;Sony 与 LG 则分别维护自己的频道字段。TVFactory 负责创建电视,是连接两侧的辅助对象。
| 具体类名 | 对应角色 | 职责 |
|---|---|---|
RemoteControl |
抽象层 | 持有 TV tv 与 TVFactory tvFactory,将开关机和频道操作委托给电视。 |
GenericRemote |
扩展抽象 | 提供 nextChannel()、prevChannel(),组合读取频道与设置频道。 |
SpecialRemote |
扩展抽象 | 提供 up()、down(),同样复用基类的频道操作。 |
TV |
实现层抽象类 | 声明 on()、off()、tuneChannel() 和 getChannel()。 |
Sony |
具体实现 | 用 station 字段保存频道,实现电视操作。 |
LG |
具体实现 | 用 channel 字段保存频道,实现电视操作。 |
TVFactory |
实现对象工厂 | 根据 getTV(String type) 的参数创建对应品牌的电视。 |
Client |
示例入口 | 演示 SpecialRemote 搭配 Sony、GenericRemote 搭配 LG。 |
示例 UML¶
classDiagram
direction TB
class RemoteControl {
<<abstract>>
TV tv
TVFactory tvFactory
+on()
+off()
+setChannel(int channel)
+getChannel() int
+setTV(String type)
}
class GenericRemote {
+nextChannel()
+prevChannel()
}
class SpecialRemote {
+up()
+down()
}
class TV {
<<abstract>>
+on()*
+off()*
+tuneChannel(int channel)*
+getChannel() int*
}
class Sony {
int station
+tuneChannel(int channel)
+getChannel() int
}
class LG {
int channel
+tuneChannel(int channel)
+getChannel() int
}
class TVFactory {
+getTV(String type) TV
}
class Client {
+main(String[] args)$
}
RemoteControl <|-- GenericRemote
RemoteControl <|-- SpecialRemote
TV <|-- Sony
TV <|-- LG
RemoteControl --> TV : tv 委托
RemoteControl --> TVFactory : tvFactory 选择电视
TVFactory ..> Sony : 创建
TVFactory ..> LG : 创建
Client ..> SpecialRemote : 使用
Client ..> GenericRemote : 使用
Client ..> TVFactory : 创建并传入
图中省略构造器、异常处理和具体电视重复的开关机方法。RemoteControl 到 TV 的关联是桥接的核心;工厂只负责构造实现对象,遥控器的操作始终经由 TV 抽象类委托。
协作过程¶
Client创建TVFactory,将它传入SpecialRemote的构造器,再调用setTV("Sony");工厂创建Sony,由遥控器保存到tv字段。remoteSony.on()经RemoteControl.on()转交给Sony.on()。SpecialRemote.up()先通过getChannel()读取当前频道,再通过setChannel()将加一后的频道传给电视的tuneChannel();down()对应减一。- 入口随后创建
GenericRemote,调用setTV("LG"),用nextChannel()、prevChannel()完成同类操作。两种遥控器都能通过setTV()选择工厂支持的品牌,使用前需要先完成这一步。
示例源码¶
| TV.java | |
|---|---|
| TVFactory.java | |
|---|---|
源码来源:Head First Design Patterns / bridge/remote。
