桥接模式(结构型)
模式概述
作为一种结构型设计模式,桥接模式可将一个大类或一系列紧密相关的类拆分为抽象和实现两种不同的层次结构,做到各自独立使用。
问题
假设现有一个名为Shape的类,可以从它扩展出两个子类:Circle和Square。但现在需要对这个继承结构进行扩展,让这些形状成为立体模型。
所以需要创建四个类才能覆盖到所有组合。

方案
问题的根本原因是:试图在两个独立的层次,也就是形状与维度上扩展形状类Shape。
桥接模式通过将继承改为组合的方式来解决这个问题。具体来说,就是让其中某个层次独立,这样就可以在初始类中引用这个新层次的对象,从而将类的状态和行为分离。

通过这种方式,可以将维度相关的代码抽取到球形
和立方体
两个子类中,然后在形状类中添加一个指向某一维度对象的引用成员变量。
这样一来,形状类可以将所有与维度相关的工作都委派给维度成员变量——这就是形状
和维度
之间的桥梁。
新增任何维度将不再需要修改形状的类继承层次,反之亦然。
结构

实现
Java
/**
* 所有设备的通用接口
*
*/
public interface Device {
boolean isEnabled();
void enable();
void disable();
int getChannel();
void setChannel(int channel);
}
/**
* 收音机
*
*/
public class Radio implements Device {
private boolean on = false;
private int channel = 1;
@Override
public boolean isEnabled() {
return on;
}
@Override
public void enable() {
on = true;
}
@Override
public void disable() {
on = false;
}
@Override
public int getChannel() {
return channel;
}
@Override
public void setChannel(int channel) {
this.channel = channel;
}
}
/**
* 电视机
*
*/
public class Tv implements Device {
private boolean on = false;
private int channel = 1;
@Override
public boolean isEnabled() {
return on;
}
@Override
public void enable() {
on = true;
}
@Override
public void disable() {
on = false;
}
@Override
public int getChannel() {
return channel;
}
@Override
public void setChannel(int channel) {
this.channel = channel;
}
}
/**
* 所有远程控制器的通用接口
*
*/
public interface Remote {
void power();
void channelDown();
void channelUp();
}
/**
* 远程控制器
*
*/
public class RemoteController implements Remote {
protected Device device;
public BasicRemote() {}
public BasicRemote(Device device) {
this.device = device;
}
@Override
public void power() {
System.out.println("电源开关");
if (device.isEnabled()) {
device.disable();
} else {
device.enable();
}
}
@Override
public void channelUp() {
System.out.println("频道上移");
device.setChannel(device.getChannel() + 1);
}
@Override
public void channelDown() {
System.out.println("频道下移");
device.setChannel(device.getChannel() - 1);
}
}
/**
* 客户端
*
*/
public class Demo {
public static void testDevice(Device device) {
System.out.println("测试远程控制器");
RemoteController remote = new RemoteController(device);
remote.power();
remote.channelUp();
remote.channelDown();
}
public static void main(String[] args) {
testDevice(new Radio());
testDevice(new Tv());
}
}
Go
package main
import "fmt"
/**
* 计算机接口
*
*/
type Computer interface {
Print()
SetPrinter(Printer)
}
/**
* Mac计算机
*
*/
type Mac struct {
printer Printer
}
func (m *Mac) Print() {
fmt.Println("Mac打印请求")
m.printer.PrintFile()
}
func (m *Mac) SetPrinter(p Printer) {
m.printer = p
}
/**
* Windows计算机
*
*/
type Windows struct {
printer Printer
}
func (w *Windows) Print() {
fmt.Println("Windows打印请求")
w.printer.PrintFile()
}
func (w *Windows) SetPrinter(p Printer) {
w.printer = p
}
/**
* 打印机接口
*
*/
type Printer interface {
PrintFile()
}
/**
* 具体打印机
*
*/
type Epson struct {
}
func (p *Epson) PrintFile() {
fmt.Println("通过Epson打印机打印")
}
type Hp struct {
}
func (p *Hp) PrintFile() {
fmt.Println("通过Hp打印机打印")
}
/**
* 客户端
*
*/
func main() {
hpPrinter := &Hp{}
epsonPrinter := &Epson{}
macComputer := &Mac{}
macComputer.SetPrinter(hpPrinter)
macComputer.Print()
fmt.Println()
macComputer.SetPrinter(epsonPrinter)
macComputer.Print()
fmt.Println()
winComputer := &Windows{}
winComputer.SetPrinter(hpPrinter)
winComputer.Print()
fmt.Println()
winComputer.SetPrinter(epsonPrinter)
winComputer.Print()
fmt.Println()
}
适用场景
当需要拆分或重组一个具有多重功能的庞杂类时,可以考虑桥接模式。
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
如果希望在几个独立维度上扩展同一个类,就必须使用桥接模式。
优缺点
桥接模式的优点。
当扩展抽象部分或实现部分时,它们之间不会相互影响,符合
开闭原则
。抽象部分专注于处理高层逻辑,实现部分处理实现细节,符合
单一职责原则
。客户端代码仅与高层抽象部分进行互动,与具体实现解耦。
桥接模式的缺点。
- 桥接模式要求正确识别出系统中独立变化的维度,其使用范围具有一定的局限性。
相关性
桥接模式
通常会用于开发前期,使程序的各个部分能够独立开来以便开发;而适配器模式
通常在已有程序中使用,让不同的类之间兼容。如果由
桥接模式
定义的抽象只与特定实现合作,那么抽象工厂模式
可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。桥接模式
、状态模式
、策略模式
和适配器模式
较为相似,它们都基于组合模式
机制——将工作委派给其他对象来解决问题。
感谢支持
更多内容,请移步《超级个体》。