桥接模式 Bridge
一. 分析传统模式下可能存在的问题
案例: 根据品牌,与样式操作手机,品牌现有"小米",“华为”,手机样式有"折叠",“滑盖”,手机功能"开机",“关机”
- 在传统模式下根据品牌样式的不同,你可能会创建不同的实体类"小米折叠开机",“小米折叠关机”, “华为折叠开机”,“华为折叠”…
- 可能存在的问题: 需要创建比较多的实体类,假设此时要增加一个"全屏"样式,是不是对应品牌再创建"小米全屏",“华为全屏”? 假设增加一个品牌,是不是要增加"xxx折叠",“xxx滑盖”,“xxx全屏”,关键还有开关机?
二. 桥接模式
是一种结构型设计模式,通过封装,聚合及继承,合成复用,把抽象与具体实现剥离,保持各部分的独立性,扩展性,主要关注的是基于类的最小设计原则,这里的抽象不是指 抽象类 或 接口 这种概念,实现 也不是 继承 或 接口实现 ,抽象与实现是指两种不同的维度,实现一般是指功能动作的执行,抽象是指对根据抽取所有公共的,里面包含了这里的实现这个维度
我的理解是:
- 首先分析功能行为与产品维度,此处的功能行为是开机关机,提供公共的父接口,将开机关机在父接口中定义为抽象方法,每个具体产品继承这个父类,通过具体产品调用执行功能行为
- 产品维度是"品牌",“样式”,根据维度创建不同的类,你可以将某一个维度视为"最终维度"例如"品牌",得出该维度时,就可以创建出产品对象,根据这个维度创建具体产品类,具体产品类继承功能父接口,并根据需求重写父类中实现功能的方法,功能父接口就是具体的产品引用,感觉此处的维度称为"最终维度",实现功能父接口的产品类称为"具体产品",并不太恰当,因为在实际调用时,上层的调用执行并不会通过这个"具体产品"来执行,而是通过另外一个维度的的类对象间接的通过"具体产品"来调用执行
- 另外的维度例如"样式",你可以看为"桥接者",桥接者中持有最终维度(父引用也就是此处定义了功能方法的Brand接口),桥接者实例化时需要初始化赋值持有的最终维度对象,在客户端调用,执行功能等操作,都是由桥接者来完成,桥接者也提供功能方法,而方法中具体实现是通过持有的最终维度确定了的产品对象来调用的
- 总结: 分析功能与产品维度,功能与产品维度剥离,根据维度拆分创建"具体产品类",与"桥接者产品类",创建具体产品父接口,接口中定义执行功能的方法,所有"具体产品"实现这个父接口并根据需求实现功能方法,“桥接者产品类"持有"具体产品”,实例化"桥接者产品"必须对"具体产品"进行初始化赋值,"桥接者产品"中定义执行功能的方法,具体的功能执行时通过持有的"具体产品"对象来调用执行的
实现步骤
- 根据维度与行为拆分,此处有两个维度"品牌",“样式”, 行为是开机关机,抽象层与实现层剥离,创建品牌对象,为实现者, 将行为封装到品牌对象中(要分析哪个是抽象层,哪个是实现层)
//品牌
interface Brand{
//开机
void open();
//关机
void close();
}
- 创建具体产品
//小米品牌具体实现
class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米开机");
}
@Override
public void close() {
System.out.println("小米关机");
}
}
//华为品牌具体实现
class HuaWei implements Brand{
@Override
public void open() {
System.out.println("华为开机");
}
@Override
public void close() {
System.out.println("华为关机");
}
}
- 创建抽象桥接者"样式",样式中聚合持有一个品牌对象,在实例化样式创建出产品的时候需要指定品牌,实例化具体产品
//样式抽象
abstract class Phone{
//样式中聚合持有一个品牌对象
private Brand brand;
//在通过实现子类进行实例化时需要传递一个品牌对象
public Phone(Brand brand) {
this.brand = brand;
}
public void open() {
this.brand.open();
}
public void close() {
this.brand.close();
}
}
- 在具体桥接者中通过持有的品牌实例具体产品调用行为方法
//样式具体实现
//折叠
class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
//重新抽象父类中的方法,通过持有的品牌对象调用行为方法
//并增加自己样式相关的代码
public void open() {
super.open();
System.out.println("折叠手机");
}
public void close() {
super.close();
System.out.println("折叠手机");
}
}
//滑盖
class SlidePhone extends Phone{
public SlidePhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println("滑盖手机");
}
public void close() {
super.close();
System.out.println("滑盖手机");
}
}
- 客户端调用
public class Test {
public static void main(String[]args) {
//通过两个维度确定获取的产品(样式+品牌)
//创建小米品牌
Brand xBrand = new XiaoMi();
//通过样式跟品牌,创建产品(实例化样式对象需要传递品牌)
Phone xFolded = new FoldedPhone( xBrand );
//调用方法
xFolded.open();
//华为
Brand hBrand = new HuaWei();
Phone hSide = new SlidePhone(hBrand);
hSide.open();
}
}
- UML图,可以清楚的看到优点,方便后续的扩展
- 桥接模式的应用案例: JDBC中的驱动程序,银行转账: 转账分类(实现层): 网上转账,柜台转账,ATM转账,转账用户(抽象层): 普通用户,银卡用户,金卡用户; 消息系统: 消息类型(抽象层): 及时消息,延时消息,消息分类(实现层):手机短信,邮件消息,返回web端提醒消息
业务与设计模式落地案例
- 在一个智能电视系统中,需要将电视和遥控器进行桥接。具体来说,可以定义一个抽象的电视类和遥控器类,并通过将它们进行桥接,来实现不同品牌电视和遥控器之间的兼容性。这样,就能够使用户在使用不同品牌电视时,能够使用同一个遥控器进行控制
- 定义一个抽象的电视类 TV 和遥控器类 RemoteControl,它们分别包含一些基本的属性和方法
public abstract class TV {
protected String brand;
protected String model;
public TV(String brand, String model) {
this.brand = brand;
this.model = model;
}
public String getBrand() {
return brand;
}
public String getModel() {
return model;
}
public abstract void powerOn();
public abstract void powerOff();
}
public abstract class RemoteControl {
protected String brand;
protected String model;
public RemoteControl(String brand, String model) {
this.brand = brand;
this.model = model;
}
public String getBrand() {
return brand;
}
public String getModel() {
return model;
}
public abstract void turnOn();
public abstract void turnOff();
}
- 定义具体的电视类和遥控器类,并将它们作为两个独立的维度进行设计。例如,对于智能电视,可以定义一个SmartTV类,它继承自TV类,并包含一个wifi属性表示是否支持无线网络;同时需要定义一个UniversalRemoteControl类,它继承自RemoteControl类,并包含一个bluetooth属性表示是否支持蓝牙控制
public class SmartTV extends TV {
private boolean wifi;
public SmartTV(String brand, String model, boolean wifi) {
super(brand, model);
this.wifi = wifi;
}
public boolean isWifi() {
return wifi;
}
@Override
public void powerOn() {
System.out.println("Smart TV " + brand + " " + model + " is turning on...");
}
@Override
public void powerOff() {
System.out.println("Smart TV " + brand + " " + model + " is turning off...");
}
}
public class UniversalRemoteControl extends RemoteControl {
private boolean bluetooth;
public UniversalRemoteControl(String brand, String model, boolean bluetooth) {
super(brand, model);
this.bluetooth = bluetooth;
}
public boolean isBluetooth() {
return bluetooth;
}
@Override
public void turnOn() {
System.out.println("Universal remote control " + brand + " " + model + " is turning on...");
}
@Override
public void turnOff() {
System.out.println("Universal remote control " + brand + " " + model + " is turning off...");
}
}
- 定义一个桥接接口 TVRemoteControlBridge,它将电视和遥控器关联起来。具体实现中,需要让电视类和遥控器类分别实现这个接口,并在实现的方法中进行相应的操作
public interface TVRemoteControlBridge {
void powerOn();
void powerOff();
void turnOn();
void turnOff();
}
public class SmartTVUniversalRemoteControlBridge implements TVRemoteControlBridge {
private SmartTV smartTV;
private UniversalRemoteControl universalRemoteControl;
public SmartTVUniversalRemoteControlBridge(SmartTV smartTV, UniversalRemoteControl universalRemoteControl) {
this.smartTV = smartTV;
this.universalRemoteControl = universalRemoteControl;
}
@Override
public void powerOn() {
smartTV.powerOn();
}
@Override
public void powerOff() {
smartTV.powerOff();
}
@Override
public void turnOn() {
universalRemoteControl.turnOn();
}
@Override
public void turnOff() {
universalRemoteControl.turnOff();
}
}
- 请求接口: 定义了两个POST请求方法,分别用于打开电视和关闭电视。在方法中,解析请求参数并根据请求中传递的品牌、型号等信息创建相应的电视和遥控器对象,然后使用桥接实现类将其桥接起来,并调用其方法执行打开或关闭操作。最后返回响应结果
@RestController
@RequestMapping("/tv-remote-control")
public class TVRemoteControlController {
@PostMapping("/turn-on")
public String turnOnTV(@RequestBody TVRemoteControlRequest request) {
// 获取请求中传递的电视信息和遥控器信息,并进行桥接操作
TV tv = new SmartTV(request.getTvBrand(), request.getTvModel(), request.isWifi());
RemoteControl remoteControl = new UniversalRemoteControl(request.getRemoteControlBrand(), request.getRemoteControlModel(), request.isBluetooth());
TVRemoteControlBridge bridge = new SmartTVUniversalRemoteControlBridge((SmartTV) tv, (UniversalRemoteControl) remoteControl);
// 执行操作并返回响应
bridge.powerOn();
bridge.turnOn();
return "Turned on the TV!";
}
@PostMapping("/turn-off")
public String turnOffTV(@RequestBody TVRemoteControlRequest request) {
// 获取请求中传递的电视信息和遥控器信息,并进行桥接操作
TV tv = new SmartTV(request.getTvBrand(), request.getTvModel(), request.isWifi());
RemoteControl remoteControl = new UniversalRemoteControl(request.getRemoteControlBrand(), request.getRemoteControlModel(), request.isBluetooth());
TVRemoteControlBridge bridge = new SmartTVUniversalRemoteControlBridge((SmartTV) tv, (UniversalRemoteControl) remoteControl);
// 执行操作并返回响应
bridge.powerOff();
bridge.turnOff();
return "Turned off the TV!";
}
}