引子
小帅最近遇到一件心烦事,他和朋友合伙开了一家公司,结果朋友背着他接私活。小帅一气之下想要散伙,但是对方就是不承认,而且公司内部的股权结构又错综复杂,小帅决定去法院起诉他,想找个对经济案件熟悉的律师帮忙。
小美最近也不开心,她在微博的上的一张自拍照,被一家无良公司拿去做了活动海报,无意间被她发现。小美去那个公司讨个说法,对方对她敷衍了事,就是不愿道歉赔偿,小美决定找个擅长侵权案件的民事律师去法院起诉这家公司。
术业有专攻,法律涉及到生活的方方面面,按照各自擅长的业务分类,律师可分为民事律师、刑事律师,经济律师,知识产权律师,涉外律师等等。
小帅和小美各自要找经济律师和民事律师,如果下次遇到知识产权问题还要找知识产权律师,但是身边又没有这么多的律师朋友,没办法同时和这么多律师都保持联系。
这时候,小帅和小美就可以找律师事务所,律师事务所里有很多类型的律师替你服务,碰到什么类型的案件,就找什么类型的律师。
如果没有律师事务所,小帅和小美,或者其他人小王和小张就要和各种各样的律师保持打交道保持联系,说不定哪天就用到人家了呢,不同的案件还要找不同的律师来代理。
有了律师事务所就不一样了,我们只需要和律师事务所打交道,需要什么样的律师让律师事务所指派给我们就行了。这里的市民和律师事务所里的律师是可以独立变化的,比如:增加了市民小王和小张;增加了知识产权律师,涉外律师都不会影响对方。
这种情况就是我下面要讲的桥接模式。
桥接模式
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式。
弄懂定义中“抽象”和“实现”两个概念,是理解它的关键。定义中的“抽象”,指的并非“抽象类”或“接口”,而是被抽象出来的一套“类库”,它只包含骨架代码,真正的业务逻辑需要委派给定义中的“实现”来完成。而定义中的“实现”,也并非“接口的实现类”,而是一套独立的“类库”。“抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。(极客时间-设计模式之美)
"将抽象部分与它的实现部分分离”这句话比较拗口,这里说的抽象部分就是市民,实现部分就是律师事务所。
“使它们都可以独立地变化”就是说市民的变化和律师事务所里律师的变化不会互相影响。
示例代码如下:
律师代理人(相当于律师事务所)类:
/**
* 律师代理人(相当于律师事务所)
*/
public interface Lawyer {
/**
* 辩论
*/
public void debate();
}
经济律师类:
/**
* 经济律师
*/
public class EconomicLawyer implements Lawyer{
/**
* 辩论
*/
@Override
public void debate() {
System.out.println("经济律师在法庭上进行经济案件辩论。");
}
}
民事律师类:
/**
* 民事律师
*/
public class CivilLawyer implements Lawyer{
/**
* 辩论
*/
@Override
public void debate() {
System.out.println("民事律师在法庭上进行民事案件辩论。");
}
}
市民类:
/**
* 市民
*/
public abstract class Citizen {
/**
* 律师代理人
*/
protected Lawyer lawyer;
public Citizen(Lawyer lawyer) {
this.lawyer = lawyer;
}
/**
* 出庭
*/
public abstract void appearInCourt();
}
小美:
/**
* 小美
*/
public class XiaoMei extends Citizen{
public XiaoMei(Lawyer lawyer) {
super(lawyer);
}
/**
* 出庭
*/
@Override
public void appearInCourt() {
System.out.println("小美出庭:");
super.lawyer.debate();
}
}
小帅:
/**
* 小帅
*/
public class XiaoShuai extends Citizen{
public XiaoShuai(Lawyer lawyer) {
super(lawyer);
}
/**
* 出庭
*/
@Override
public void appearInCourt() {
System.out.println("小帅出庭:");
super.lawyer.debate();
}
}
测试:
/**
* 法庭
*/
public class Court {
public static void main(String[] args) {
// 小帅通过律师事务所找到经济律师
Citizen xiaoShuai = new XiaoShuai(new EconomicLawyer());
// 小帅出庭
xiaoShuai.appearInCourt();
// 小美通过律师事务所找到民事律师
Citizen xiaoMei = new XiaoMei(new CivilLawyer());
// 小美出庭
xiaoMei.appearInCourt();
}
}
输出:
小帅出庭:
经济律师在法庭上进行经济案件辩论。
小美出庭:
民事律师在法庭上进行民事案件辩论。
桥接模式和策略模式的区别
策略模式(Strategy)的与桥接模式(Bridge)看起来很像,其实它们之间有很大的区别:策略模式改变对象的行为,而桥接模式改变类的结构。
策略模式是行为型的动态模式,解决对象和对象间的调用问题(研究如何在程序运行中改变对象的行为)。
桥接模式是一种结构型的模式,解决类和类之间的结构问题(研究如何重新构建类的结构)。
这两种设计模式的意图是不相同的。
总结
如果你不希望使用继承或因为多层次继承导致系统类的个数急剧增加,我们可以考虑使用桥接模式,把两个(或多个)独立变化的维度,通过组合的方式,让这两个(或多个)维度可以独立进行扩展。
再举个例子说明一下,比如汽车有两个纬度,一个是品牌:奔驰、宝马、奥迪等等,还有一个是档位:自动挡、手动挡、手自一体等等。
如果按照继承的设计模式,假设有M个车品牌,N个档位一共要写M*N个类去描述所有车和档位的组合,就会造成类的爆炸。
如果我们用桥接模式的话,就可以让品牌和档位独立的变化,使用的时候把品牌和档位组合起来用就行了,只需要M+N个类就行了。
桥接模式就是将系统各个独立的纬度都抽离出来,然后使用时按需组合。
下面我们来看看桥接模式的优缺点:
优点
- 符合开闭原则:提高了系统的可扩展性,可以新增抽象部分和实现部分,在两个变化维度中任意扩展一个维度,都不需要修改原有系统, 且它们之间不会相互影响。
- 实现通过组合关系来替代继承关系,避免继承层次的指数级爆炸。
缺点
- 对高内聚的类使用该模式可能会让代码更加复杂。