15 Bridge Pattern(桥接模式)
前言:把实现分开,让它们各自变化
需求:
麦当当和肯打基是Vander小时候的最爱,Vander发达之后,他也想加盟它们去分一杯羹,现在合同已经谈拢了,Vander开始加盟它们制作食物,他很快就开始了设计,设计如下:
Restaurant:
public abstract class Restaurant {
protected abstract void makeFood();
}
Kentucky:
public abstract class Kentucky extends Restaurant {
}
MacDonald:
public abstract class MacDonald extends Restaurant {
}
KentuckyFriedChickenFactory:
public class KentuckyFriedChickenFactory extends Kentucky {
// 获取父类的名字(super.getClass().getSimpleName()依然还是获取当前类的名字)
private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
public void makeFood() {
System.out.println("生产" + RESTAURANT_NAME + "的炸鸡");
}
}
MacDonaldFriedChickenFactory:
public class MacDonaldFriedChickenFactory extends MacDonald {
private final String RESTAURANT_NAME = super.getClass().getSuperclass().getSimpleName();
public void makeFood() {
System.out.println("生产" + RESTAURANT_NAME + "的炸鸡");
}
}
KentuckyHamburgFactory:
public class KentuckyHamburgFactory extends Kentucky {
private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
public void makeFood() {
System.out.println("生产" + RESTAURANT_NAME + "的汉堡");
}
}
MacDonaldHamburgFactory:
public class MacDonaldHamburgFactory extends MacDonald {
private final String RESTAURANT_NAME = this.getClass().getSuperclass().getSimpleName();
public void makeFood() {
System.out.println("生产" + RESTAURANT_NAME + "的炸鸡");
}
}
首先肯打鸡跟麦当当都是Restaurant,很理所当然地实现Restaurant接口,肯打鸡生产汉堡的工厂跟生产炸鸡的工厂都继承Kentucky,类似的麦当当也是类似的情况,接下来Vander听说汉堡仔的汉堡跟薯条都非常好吃,所以也想加盟其中,然后又在Restaurant基础上继续加入,并且需要添加薯条这种食物,接着三家餐厅都得加入炸薯条工厂生产炸薯条,这太累了,Vander想有没有更好的方法呢。
此时Panda大师又来了,她一看就觉得这么设计其实有个很大的问题。
Panda:你这样的设计用了继承来实现,但是继承的关系在编译时就定义好了,所以没法在运行的时候改变父类继承的实现,意思就是说你没法在运行的时候指定用哪个工厂生产哪种种类的食物。这里就涉及到以前的一个原则,合成/聚合复用
原则:尽量使用合成/聚合,尽量不要使用类继承去实现。
Vander:具体应该怎么操作呢?不明白合成跟聚合是什么,能不能举个例子
Panda:请看以下的类图,马聚合成马群(聚合),而四肢是属于马的一部分(合成)
Panda:使用这个原则的好处就是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上,这样累和类继承层次会保持较小规模,并且不太可能增长成为不可控制的庞然大物。说了这么多你可以试试以下的设计:
Restaurant:
public abstract class Restaurant {
private FoodFactory foodFactory;
protected void makeFood() {
foodFactory.makeFood();
}
public void setFoodFactory(FoodFactory foodFactory) {
this.foodFactory = foodFactory;
}
}
MacDonald:
public class MacDonald extends Restaurant {
private final String RESTAURANT_NAME = this.getClass().getSimpleName();
public MacDonald() {
System.out.println(RESTAURANT_NAME + ":");
}
}
Kentucky:
public class Kentucky extends Restaurant {
private final String RESTAURANT_NAME = this.getClass().getSimpleName();
public Kentucky() {
System.out.println(RESTAURANT_NAME + ":");
}
}
FoodFactory:
public abstract class FoodFactory {
public abstract void makeFood();
}
FriedChickenFactory:
public class FriedChickenFactory extends FoodFactory {
public void makeFood() {
System.out.println(" 生产炸鸡");
}
}
HamburgFactory:
public class HamburgFactory extends FoodFactory {
public void makeFood() {
System.out.println(" 生产汉堡");
}
}
OrderFood:
public class OrderFood {
public static void main(String[] args) {
Restaurant restaurant;
restaurant = new MacDonald();
restaurant.setFoodFactory(new FriedChickenFactory());
restaurant.makeFood();
restaurant.setFoodFactory(new HamburgFactory());
restaurant.makeFood();
restaurant = new Kentucky();
restaurant.setFoodFactory(new FriedChickenFactory());
restaurant.makeFood();
restaurant.setFoodFactory(new HamburgFactory());
restaurant.makeFood();
}
}
实现效果:
现在这么设计有个好处,就是如果要加盟一个新的餐厅,只需要实现多一个Restaurant接口类,如果要添加多一种食物,只需要实现多一个对应的食物的工厂就完事了,事实上是利用了前面学到的“单一责任原则”,让创建食物的工作交给了食物工厂,然后通过Restaurant来指定是哪个餐厅的食物,而不是原来那样将哪个餐厅的哪种食物都糅合在一起了,相当于本来的食物工厂又加入了餐厅的元素,导致了一个类有两种变化的可能。
下面给桥接模式下个定义:
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
简单说明一下这里的抽象部分和实现部分,并不是说抽象类跟实现类的分离,而是说实现的部分就是变化的原因,其实就是将变化的原因独立出来,让它们各自去变化,不互相影响。也就是上面的例子中,餐厅的变化跟食物的变化要独立开来。
最后又到了喜闻乐见的总结部分,我们又来总结我们现在现有的设计模式武器。
面向对象基础
抽象、封装、多态、继承
九大设计原则
设计原则一:封装变化
设计原则二:针对接口编程,不针对实现编程
设计原则三:多用组合,少用继承
设计原则四:为交互对象之间的松耦合设计而努力
设计原则五:对扩展开放,对修改关闭
设计原则六:依赖抽象,不要依赖于具体的类
设计原则七:只和你的密友谈话
设计原则八:别找我,我有需要会找你
设计原则九:类应该只有一个改变的理由
模式
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。