Bridge模式的用意是“将抽象化(Abstraction)与实现化(Implementation)解耦,使得二者可以独立地变化”。
应用场景
在以下的场景下应当使用桥梁模式:
(1)如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
(2)设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
(3)一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
(4)虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
结构模式
由上图可以看出,桥梁模式有两个等级结构:
(1)左侧的由抽象化角色和修正抽象化角色组成的抽象化等级结构。
(2)由实现化角色和两个具体实现化角色所组成的实现化等级结构。
其中由UML图可以看出该模式所涉及的角色有:
(1)抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
(2)修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
(3)实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应该只给出底层操作,而抽象化角色应该只给出基于底层操作的更高一层的操作。
(4)具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。
抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身,这也就是此模式别名“柄体”的来源。
代码实现
该模式的实现框架如下所示:
<span style="font-size:14px;">abstract class Abstraction
{
protected Implementor imp;
public void operation()
{
imp.operationImp();
}
}
class RefinedAbstraction extends Abstraction
{
public void operation()
{
//improved logic
}
}
abstract class Implementor
{
public abstract void operationImp();
}
class ConcreteImplementorA extends Implementor
{
public void operationImp()
{
System.out.println("Do something...");
}
}
</span>
对该模式的应用如下:
以一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、 中杯不加奶、大杯不加奶。
但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁).
实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。
那我们从分离抽象和行为的角度,使用Bridge模式来实现。
如何实现?
以上面提到的咖啡为例.
我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.
先看看抽象部分的接口代码:
<span style="font-size:14px;">public abstract class Coffee
{
CoffeeImp coffeeImp;
public void setCoffeeImp() {
this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
}
public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}
public abstract void pourCoffee();
}
</span>
其中CoffeeImp 是加不加奶的行为接口,看其代码如下:
<span style="font-size:14px;">public abstract class CoffeeImp
{
public abstract void pourCoffeeImp();
}
</span>
现在我们有了两个抽象类,下面我们分别对其进行继承,实现concreteclass:
<span style="font-size:14px;">//中杯
public class MediumCoffee extends Coffee
{
public MediumCoffee() {setCoffeeImp();}
public void pourCoffee()
{
CoffeeImp coffeeImp = this.getCoffeeImp();
//我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
for (int i = 0; i < 2; i++)
{
coffeeImp.pourCoffeeImp();
}
}
}
//大杯
public class SuperSizeCoffee extends Coffee
{
public SuperSizeCoffee() {setCoffeeImp();}
public void pourCoffee()
{
CoffeeImp coffeeImp = this.getCoffeeImp();
//我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
for (int i = 0; i < 5; i++)
{
coffeeImp.pourCoffeeImp();
}
}
}
</span>
上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:
<span style="font-size:14px;">//加奶
public class MilkCoffeeImp extends CoffeeImp
{
MilkCoffeeImp() {}
public void pourCoffeeImp()
{
System.out.println("加了美味的牛奶");
}
}
//不加奶
public class FragrantCoffeeImp extends CoffeeImp
{
FragrantCoffeeImp() {}
public void pourCoffeeImp()
{
System.out.println("什么也没加,清香");
}
}
</span>
Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶
看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:
<span style="font-size:14px;">public class CoffeeImpSingleton
{
private static CoffeeImp coffeeImp;
public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
{this.coffeeImp = coffeeImpIn;}
public static CoffeeImp getTheCoffeeImp()
{
return coffeeImp;
}
}
</span>
看看中杯加奶和大杯加奶 是怎么出来的:
//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(newMilkCoffeeImp());
//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();
//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();
注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键。