桥梁模式【Bridge Pattern 】
以公司生产产品为例子。一个房地产公司,一个山寨公司,山寨公司什么好赚就生产什么。我们先进行这样的设计。看类图:
公司Crop先定义如下:
public abstract class Corp {
/*
* 是公司就应该有生产把,甭管是什么软件公司还是制造业公司
* 那每个公司的生产的东西都不一样,所以由实现类来完成
*/
protected abstract void produce();
/*
* 有产品了,那肯定要销售呀,不销售你公司怎么生存
*/
protected abstract void sell();
//公司是干什么的?赚钱的呀,不赚钱傻子才干
public void makeMoney(){
//每个公司都是一样,先生产
this.produce();
//然后销售
this.sell();
}
}
山寨IPod定义如下:
public class IPodCorp extends Corp {
//我开始生产iPod了
protected void produce() {
System.out.println("我生产iPod...");
}
//山寨的iPod很畅销,便宜呀
protected void sell() {
System.out.println("iPod畅销...");
}
//狂赚钱
public void makeMoney(){
super.makeMoney();
System.out.println("我赚钱呀...");
}
}
在Client中进行调用:
HouseCorp houseCorp =new HouseCorp();
houseCorp.makeMoney();
ClothesCorp clothesCorp = new ClothesCorp();
clothesCorp.makeMoney();
这样子算是实现了,但是想想山寨公司什么好赚就往哪里发展那么他之前的设备和员工各方面的资源又该何去何从呢?这样的设计就显得不合理了,那么再进行改善。看一下改善后的类图:
在Corp 类和 Product 类建立一个关联关系
Product定义如下:
public abstract class Product {
//甭管是什么产品它总要是能被生产出来
public abstract void beProducted();
//生产出来的东西,一定要销售出去,否则亏本呀
public abstract void beSelled();
}
再看下IPod类:
public class IPod extends Product {
public void beProducted() {
System.out.println("生产出的iPod是这个样子的...");
}
public void beSelled() {
System.out.println("生产出的iPod卖出去了...");
}
}
于是公司Crop可以定义如下:
public abstract class Corp {
//定义一个产品对象,抽象的了,不知道具体是什么产品
private Product product;
//构造函数,由子类定义传递具体的产品进来
public Corp(Product product){
this.product = product;
}
//公司是干什么的?赚钱的呀,不赚钱傻子才干
public void makeMoney(){
//每个公司都是一样,先生产
this.product.beProducted();
//然后销售
this.product.beSelled();
}
}
那么山寨公司就可以定义如下:
public class ShanZhaiCorp extends Corp {
//产什么产品,不知道,等被调用的才知道
public ShanZhaiCorp(Product product){
super(product);
}
//狂赚钱
public void makeMoney(){
super.makeMoney();
System.out.println("我赚钱呀...");
}
}
通过一个有参的构造函数,这样便可以生产不同的产品了,只需在Client中传入不同的参数便可以进行不同的生产了。
ShanZhaiCorp shanZhaiCorp = new ShanZhaiCorp(new Clothes());
shanZhaiCorp.makeMoney();
从上面的分析来看,我们主要是将Crop和Product进行扩展,而不必对整个应用进行多大的变更,这就是桥梁模式。
上面的案例,把Corp类以及它的两个实现类放到了Abstraction包中, 把House以及相关的三个实现类放到了 Implementor 包中,这两个包分别对应了桥梁模式的业务抽象角色(Abstraction) 和业务实现角色 (Implementor) 。这两个角色大家只要记住一句话就成:业务抽象角色引用业务实现角色,或者说业务抽象角色的部分实现是由业务实现角色完成的。
总结:
(1)桥梁模式的优点就是类间解耦,我们上面已经提到,两个角色都可以自己的扩展下去,不会相互影响,这个也符合 OCP 原则。
(2)注意桥梁模式与继承的对比。如果对于比较明确不发生变化的,则通过继承来完成,若不能确定是否会发生变化的,那就认为是会发生变化,则通过桥梁模式来解决。
桥梁模式的使用场景:
● 不希望或不适用使用继承的场景
例如继承层次过渡、无法更细化设计颗粒等场景,需要考虑使用桥梁模式。
● 接口或抽象类不稳定的场景
明知道接口不稳定还想通过实现或继承来实现业务需求,那是得不偿失的,也是比较失败的做法。
● 重用性要求较高的场景
设计的颗粒度越细,则被重用的可能性就越大,而采用继承则受父类的限制,不可能出现太细的颗粒度。