桥接模式(GOF):指将抽象部分和实现部分分开,让它们各自随意增加减少。而不受其它约束。
适用环境:
u 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
u 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
u 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
u (C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。
u 有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。Rumbaugh 称这种类层次结构为“嵌套的普化”(nested generalizations )。
u 你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
为什么使用?
不希望抽象部分和行为有一种固定的绑定关系,而是应该可以动态联系的。
如果一个抽象类或接口有多个具体实现(子类、concrete subclass),这些子类之间关系可能有以下两种情况:
1. 这多个子类之间概念是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复。
2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为.
例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、 中杯不加奶、大杯不加奶。
但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象 和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁).
实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。
那我们从分离抽象和行为的角度,使用Bridge模式来实现。
如何实现?
以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.
先看看抽象部分的接口代码:
package bridge;
public abstract class Coffee{
CoffeeImpl coffeeImpl=null;
public void setCoffeeImpl(){
this.coffeeImpl=CoffeeImplSingleton.getCoffeeImpl();
}
public CoffeeImpl getCoffeeImpl(){
return this.coffeeImpl;
}
public abstract void pourCoffee();
} 其中CoffeeImp 是加不加奶的行为接口,看其代码如下
package bridge;
public abstract class CoffeeImpl{
public abstract void pourCoffeeImpl();
} 现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class
package bridge;
//中杯Coffee
public class MediumCoffee extends Coffee{
public MediumCoffee (){
setCoffeeImpl();
}
public void pourCoffee(){
CoffeeImpl coffeeImp=this.getCoffeeImpl();
coffeeImpl.pourCoffeeImpl();
}
} package bridge;
//大杯Coffee
public class SuperSizeCoffee extends Coffee{
public SuperSizeCoffee (){
setCoffeeImpl();
}
public void pourCoffee(){
CoffeeImpl coffeeImp=this.getCoffeeImpl();
coffeeImpl.pourCoffeeImpl();
}
} 下面对行为CoffeeImp进行继承:
package bridge;
//不加牛奶
public class FragrantCoffeeImpl extends CoffeeImpl{
public void pourCoffeeImpl(){
System.out.println("没有加牛奶的Coffee");
}
} package bridge;
//加牛奶
public class CoffeeImplSingleton{
private static CoffeeImpl coffeeImpl;
public CoffeeImplSingleton(CoffeeImpl coffeeImpl){
this.coffeeImpl=coffeeImpl;
}
public static CoffeeImpl getCoffeeImpl(){
return coffeeImpl;
}
} 最后实现:
package bridge;
public class Bridge{
public static void main(String[] args){
//中杯加奶
CoffeeImplSingleton coffeeImpl=new CoffeeImplSingleton(new MilkCoffeeImpl());
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();
//大杯不加奶
CoffeeImplSingleton coffeeImpl2=new CoffeeImplSingleton(new FragrantCoffeeImpl());
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();
}
} 运行结果:
加了牛奶的Coffee
没有加牛奶的Coffee
Press any key to continue...
与适配器模式的区别:桥接模式与对象形式的适配器模式很相似,然而适配器的目的是要改变已有的接口,让它们可以相容,以使没有关系的两个类能一起工作,而桥接模式是分离抽象化与实现化,以使两者的接口可以不同。因此,两模式是往两个相反方面努力的。
参考资料:http://www.jdon.com/designpatterns/bridge.htm(本篇的主要来源)
http://www.lvjiyong.com/books/DesignPatterns/20.html
《Java与模式》