桥模式(学习笔记)
1. 意图
将抽象部分与它的实现部分分离,使它们可以独立的变化
2. 动机
如左图所示,如果此时需求变化,假设有n个形状(圆形,方形,长方形...),增加m个颜色,则需要额外增加(n+1)*m个类,会造成类爆炸。另外,代码后期维护成本很高。桥模式使用组合代替继承来解决这个问题。这样只会增加m个新类,代码易于维护。
3. 适用性
- 不希望抽象和它的实现部分有一个固定的绑定关系
- 类的抽象以及它的实现都应该通过生成子类的方法加以扩充。这时,Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不需要重新编译
4. 结构
5. 效果
1) 分离接口及其实现部分
2) 提高可扩充性
3) 如果需要在运行时切换不同的方法,可使用桥接模式
桥模式可以替换抽象部分中的实现对象
4) 开闭原则。 可以新增抽象部分和实现部分, 且它们之间不会相互影响。
5) 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。
6. 代码实现
devices/Device.java: 所有设备的通用接口
package bridge.devices; /** * @author GaoMing * @date 2021/7/12 - 10:59 */ public interface Device { boolean isEnabled(); void enable(); void disable(); int getVolume(); void setVolume(int percent); int getChannel(); void setChannel(int channel); void printStatus(); }
devices/Radio.java: 收音机
package bridge.devices; /** * @author GaoMing * @date 2021/7/12 - 10:56 */ public class Radio implements Device{ private boolean on = false; private int volume = 30; 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 getVolume() { return volume; } @Override public void setVolume(int volume) { if (volume > 100) { this.volume = 100; } else if (volume < 0) { this.volume = 0; } else { this.volume = volume; } } @Override public int getChannel() { return channel; } @Override public void setChannel(int channel) { this.channel = channel; } @Override public void printStatus() { System.out.println("------------------------------------"); System.out.println("| I'm radio."); System.out.println("| I'm " + (on ? "enabled" : "disabled")); System.out.println("| Current volume is " + volume + "%"); System.out.println("| Current channel is " + channel); System.out.println("------------------------------------\n"); } }
devices/Tv.java: 电视机
package bridge.devices; /** * @author GaoMing * @date 2021/7/12 - 10:56 */ public class TV implements Device{ private boolean on = false; private int volume = 30; 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 getVolume() { return volume; } @Override public void setVolume(int volume) { if (volume > 100) { this.volume = 100; } else if (volume < 0) { this.volume = 0; } else { this.volume = volume; } } @Override public int getChannel() { return channel; } @Override public void setChannel(int channel) { this.channel = channel; } @Override public void printStatus() { System.out.println("------------------------------------"); System.out.println("| I'm TV set."); System.out.println("| I'm " + (on ? "enabled" : "disabled")); System.out.println("| Current volume is " + volume + "%"); System.out.println("| Current channel is " + channel); System.out.println("------------------------------------\n"); } }
remotes/Remote.java: 所有远程控制器的通用接口
package bridge.remotes; /** * @author GaoMing * @date 2021/7/12 - 11:02 */ public interface Remote { void power(); void volumeDown(); void volumeUp(); void channelDown(); void channelUp(); }
remotes/BasicRemote.java: 基础远程控制器
package bridge.remotes; import bridge.devices.Device; /** * @author GaoMing * @date 2021/7/12 - 10:57 */ public class BasicRemote implements Remote{ protected Device device; public BasicRemote() {} public BasicRemote(Device device) { this.device = device; } @Override public void power() { System.out.println("Remote: power toggle"); if (device.isEnabled()) { device.disable(); } else { device.enable(); } } @Override public void volumeDown() { System.out.println("Remote: volume down"); device.setVolume(device.getVolume() - 10); } @Override public void volumeUp() { System.out.println("Remote: volume up"); device.setVolume(device.getVolume() + 10); } @Override public void channelDown() { System.out.println("Remote: channel down"); device.setChannel(device.getChannel() - 1); } @Override public void channelUp() { System.out.println("Remote: channel up"); device.setChannel(device.getChannel() + 1); } }
remotes/AdvancedRemote.java: 高级远程控制器
package bridge.remotes; import bridge.devices.Device; /** * @author GaoMing * @date 2021/7/12 - 10:57 */ public class AdvancedRemote extends BasicRemote{ public AdvancedRemote(Device device) { super.device = device; } public void mute() { System.out.println("Remote: mute"); device.setVolume(0); } }
Demo.java: 客户端代码
package bridge; import bridge.devices.Device; import bridge.devices.Radio; import bridge.devices.TV; import bridge.remotes.AdvancedRemote; import bridge.remotes.BasicRemote; /** * @author GaoMing * @date 2021/7/12 - 10:56 */ public class Demo { public static void main(String[] args) { testDevice(new TV()); testDevice(new Radio()); } public static void testDevice(Device device) { System.out.println("Tests with basic remote."); BasicRemote basicRemote = new BasicRemote(device); basicRemote.power(); device.printStatus(); System.out.println("Tests with advanced remote."); AdvancedRemote advancedRemote = new AdvancedRemote(device); advancedRemote.power(); advancedRemote.mute(); device.printStatus(); } }
执行结果
Tests with basic remote. Remote: power toggle ------------------------------------ | I'm TV set. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------ Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm TV set. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------ Tests with basic remote. Remote: power toggle ------------------------------------ | I'm radio. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------ Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm radio. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------
7. 与其他模式的关系
- 可以结合使用生成器模式和桥接模式: 主管类负责抽象工作, 各种不同的生成器负责实现工作
- 可以将抽象工厂模式和桥接搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性
- 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作
8. 已知应用
使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用
识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别