设计模式 | 桥接模式(bridge)
定义:
将抽象部分与它的实现部分分离,使它们都可以独立地变化
结构:(书中图,侵删)
一个抽象类,用于聚合实现
若干个实现抽象类的类
一个实现的父类
若干个实现了父类的具体实现类
实例:
我想到了一个工作中的例子,通常我们需要去对接一些第三方平台。
假如最开始你需要对接淘宝平台,去创建销售单。然后过段时间又需要去对接京东平台,也是要创建销售单。
你的类结构大概是这样的:
出于面向对象考虑,你可能会给平台加个父类,变成这样:
这时候又告诉你,你需要给两个平台都添加获取订单的功能,然后变成这样:
假设接下来又要给每个平台添加创建退货单,抓取退货单,然后又新加了两个平台,amazon,ebay什么的。
那简直就是灾难。代码没法复用,复制粘贴可不算复用,虽然每个平台都会有些不一样,但是功能本身是大同小异的。
于是呢,就引出了我们的桥接模式,按照我的理解来说,就是将系统按照各个维度拆分出来,每个维度都是独立的部分,互不影响,同时通过一座桥(这里指聚合,后面会细讲)把他们连接起来。
修改之后的类结构是这样:
操作父类:
package designpattern.bridge; public abstract class Operation { public abstract void operate(); }
创建订单类:
package designpattern.bridge; public class CreateOrder extends Operation { @Override public void operate() { System.out.println("创建订单"); } }
获取订单类:
package designpattern.bridge; public class GetOrders extends Operation { @Override public void operate() { System.out.println("获取订单"); } }
平台父类(聚合类):
package designpattern.bridge; public abstract class Platform { protected Operation operation; public void setOperation(Operation operation) { this.operation = operation; } public abstract void operate(); }
淘宝类:
package designpattern.bridge; public class Taobao extends Platform { @Override public void operate() { System.out.print("淘宝->"); operation.operate(); } }
京东类:
package designpattern.bridge; public class JingDong extends Platform { @Override public void operate() { System.out.print("京东->"); operation.operate(); } }
客户端:
package designpattern.bridge; public class Client { public static void main(String[] args) { Operation createOrder=new CreateOrder(); Operation getOrder=new GetOrders(); Platform tb=new Taobao(); tb.setOperation(createOrder); tb.operate(); tb.setOperation(getOrder); tb.operate(); System.out.println("=============================="); Platform jd=new JingDong(); jd.setOperation(createOrder); jd.operate(); jd.setOperation(getOrder); jd.operate(); } }
结果输出:
淘宝->创建订单 淘宝->获取订单 ============================== 京东->创建订单 京东->获取订单
番外:
书中还提到一个原则:
然后在《effective java》这本书也看到了类似的内容,角度又有些不同:
大概说的是,子类必须依附于父类,如果父类修改了,子类必须得跟着一起改。甚至父类新增了一个和子类签名相同,但是返回类型不同的方法,会导致你的编译不通过。
解决方法,就是复合,也是上面说的合成-》新类不继承父类,而是包含一个拥有父类(虽然没有继承,为了前后统一,还是称之为父类)实例的域,通过这个实例去访问父类的方法。
上文提到的包装类是指的装饰模式(decorator),这里就不展开了,感兴趣的可以看看之前写的文章:设计模式 | 装饰模式(decorator)
总结:
核心还是解耦,以及开放-封闭原则。要想方设法的让新增功能的时候不影响之前的代码。设计模式只是表现形式不一样,内核都差不多。
桥接模式在系统出现多维度的时候就可以考虑使用,其实这种是在最开始就能预见到的,比如文中举的例子,通常都能想到未来可能增加功能和平台,所以在写第一个平台的第一个功能的时候就应该有意识的去设计。(哈哈哈,打脸来得总是这么的触不及防,刚说要提前去设计,就在下一章看见了文尾图中的内容)
最后就是在使用继承前要再三的确认是不是“is a”关系,即便是也不要轻易的使用继承,复合往往是更优先的选择。
关于上图说的命令模式,可见:设计模式 | 命令模式(Command)