简介
桥接器是一种结构型设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用。
问题
假如你有一个几何形状Shape类,它有两个子类:圆形Circle和方形Square。现在你想对类层次结构进行扩展,让它包含颜色属性,所以你打算创建 红色Red和 蓝色Blue的形状子类。但是,由于你已有两个子类,所以总共需要创建四个类才能覆盖所有组合,比如蓝色圆形BlueCircle和红色方形RedSquare。
在层次结构里新增形状和颜色都会导致代码复杂程度指数增长。比如添加三角形状,你需要新增两个子类,也就是每种颜色一个;此后新增一种新颜色需要新增三个子类,即每种形状一个。代码就越来越复杂。
解决方案
问题的根本原因是我们试图在两个独立的维度(形状与颜色)上扩展形状类。我们在使用类继承时经常遇到这样的问题。
桥接模式通过把继承改为组合的方式来解决这个问题。具体来说,就是抽取其中一个维度并让他成为独立的类层次,这样就可以在原来的类中引用这个新层次的对象,从而让一个类不需要拥有所有的状态和行为。
根据这种方法,我们可以把颜色相关的代码抽取到拥有红色和蓝色两个子类的颜色父类中,然后在形状类里添加一个指向某一颜色对象的引用成员变量。这个时候,形状类就可以把所有跟颜色相关的工作委派给引入的颜色对象。这样的引用就成为了形状和颜色之间的桥梁。在这之后,新增颜色都不再需要修改形状的类层次,反过来也一样。
示例
以上示例还比较简单,我们来看一个更复杂的示例,以便演示了桥接模式,看看它怎么拆分程序里同时管理设备和遥控器的复杂代码。设备Device类作为实现部分,而遥控器Remote类则作为抽象部分。
/* 实现层级:设备接口规范 */
public interface Device {
// 状态检测基础方法
boolean isEnabled();
void enable(); // Device接口定义
void disable();
// 媒体控制方法
int getVolume();
void setVolume(int percent);
int getChannel();
void setChannel(int channel);
}
/* 具体设备实现 */
class Tv implements Device { // TV实现
private boolean on = false;
private int volume = 50;
private int channel = 1;
public boolean isEnabled() { return on; }
public void enable() { on = true; } // TV特有启动过程
public void disable() { on = false; }
public int getVolume() { return volume; }
public void setVolume(int percent) {
volume = Math.max(0, Math.min(100, percent));
}
public int getChannel() { return channel; }
public void setChannel(int ch) {
channel = Math.max(1, ch);
}
}
class Radio implements Device { // Radio实现
private boolean active = false;
private int level = 30;
public boolean isEnabled() { return active; }
public void enable() { active = true; }
public void disable() { active = false; }
public int getVolume() { return level; }
public void setVolume(int percent) {
level = (percent > 100) ? 100 : Math.max(0, percent);
}
public int getChannel() { return 0; } // 收音机无频道
public void setChannel(int ch) {} // 空实现
}
/* 抽象层级:遥控器抽象 */
abstract class RemoteControl {
// 持有实现层对象的引用(关键结构)
protected Device device;
public RemoteControl(Device device) {
this.device = device;
}
// 电源切换核心逻辑
public void togglePower() {
if (device.isEnabled()) {
device.disable();
} else {
device.enable();
}
}
public void volumeUp() {
device.setVolume(device.getVolume() + 10);
}
public void volumeDown() {
device.setVolume(device.getVolume() - 10);
}
public void channelUp() {
device.setChannel(device.getChannel() + 1);
}
public void channelDown() {
device.setChannel(device.getChannel() - 1);
}
}
/* 扩展抽象:高级遥控器(扩展功能) */
class AdvancedRemoteControl extends RemoteControl {
public AdvancedRemoteControl(Device device) {
super(device);
}
public void mute() { // 新增静音功能
device.setVolume(0); // 通过接口操作设备
}
}
// 客户端使用示例
public class Client {
public static void main(String[] args) {
Device tv = new Tv();
RemoteControl basicRemote = new RemoteControl(tv);
basicRemote.togglePower(); // 开启电视
Device radio = new Radio();
AdvancedRemoteControl advancedRemote = new AdvancedRemoteControl(radio);
advancedRemote.mute(); // 设置静音
}
}
总结
- 抽象部分(Abstraction):提供高层控制逻辑,依赖完成底层实际工作的具体实现对象。
- 实现部分(Implementation):为所有具体实现声明通用接口。抽象部分仅能通过在这里声明的方法和实现对象交互。抽象部分可以列出和实现部分一样的方法,但是抽象部分通常声明一些复杂的行为,这些行为依赖于多种由实现部分声明的操作。
- 具体实现(Concrete Implementations)中包括特定平台的代码。
- 精确抽象(Refined Abstraction)提供控制逻辑的变体。和它的父类一样,它们通过通用实现接口与不同的实现进行交互。
- 通常情况下,客户端(Client)仅关心如何与抽象部分合作。但是,客户端需要把抽象对象和一个实现对象连接起来。