隶属类别——对象结构型模式
将抽象的部分和它的实现部分分离,使他们都可以独立变化。
Handle/Body
当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口。而具体的子类则用不同方式加以实现。但是此方法有时不够灵活,继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改,扩充和重用。
让我们考虑在一个用户界面工具箱中,一个可移植的Window抽象部分的实现。例如,这一抽象部分应该允许用户开发一些在X Window System和IBM的Presentation Manager(PM)系统中都可以使用的应用程序。运用继承机制,我们可以定义Window抽象类和它的两个子类XWindow 与PMWindow,由它们分别实现不同系统平台的Windows界面,但是继承机制有两个不足之处:
客户在创建窗口时应该不涉及到其具体实现部分。仅仅是窗口的实现部分依赖于应用运行的平台。这样客户代码在创建窗口时就不应涉及到特定的平台。
Bridge模式解决了以上问题的方法是,将Window抽象和它的实现部分分别放在地理的类层结构中。其中一个类层次结构针对窗口接口(Window,IconWindow,TransientWindow),另外一个独立的类层次结构针对平台相关的窗口实现部分,这个类层次结构的根类为WindowsImpl。例如XWindosImp提供一个基于X Window系统的实现,如图所示:
对Window子类的所有操作都是用WindowImp接口中的抽象操作实现的,这就将窗口的抽象与系统的相关实现分离开来(单一责任原则)。因此,我们1将Window与WindowImp之间的关系称为桥接,因为它在抽象类与它的实现之间起到了桥梁作用,这使他们可以独立地变化,通过委托的方式来进行方法调用,这使得调用更加灵活。
以下一些情况可以使用Bridge模式:
Bridge模式有以下一些优点:
1)分离接口及其实现部分 一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。
将Abstraction和Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类是,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。
另外,接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abastraction和Implementor即可。
Bridge模式的缺点:
使用Bridge模式需要注意以下一些问题:
在Java仅需Implementor引用设置为private。
如果Abstraction知道所有的ConcreteImplementor类,它就可以在它的构造器中对其中的一个类进行实例化,它可以通过传递给构造器的参数确定实例化哪一个类。例如,如果一个collection类支持多重实现,就可以根据collection的大小决定实例化哪一个类。链表的实现可以用于较小的collection类,而hash表则可用于就较大的collection类。
另外一个方法是首先选择一个缺省的实现,然而根据需要改变这个实现。例如,如果一个collection的大小超过了一定的阈值时,它将会切换它的实现,使之更适用于表目较多的collection。
也可以代理给另一个对象,由它一次决定。在Window/WindowImp的例子中,我们可以引入一个factory对象,该对象的唯一职责就是封装系统平台的细节。这个对象知道如何应该为所用的平台创建何种类型的WindowImpl对象;Window仅需向它请求一个WindowImp,而它会返回正确的WindowImp对象。这种方法的优点是Abstraction类不和任何一个Implementor类直接耦合。
首先创建Abstraction——Weapon.java
ppublic interface Weapon {
void wield();
void swing();
void retract();
Enchantment getEnchantment();
}
接下来是Implementor——Enchantment.java
public interface Enchantment {
void activate();
void apply();
void deactivate();
}
接着是ConcreteImplementor——FlyEnchantment.java & SoulEatingEnchantment.java
FlyEnchantment.java
public class FlyingEnchantment implements Enchantment{
@Override
public void activate() {
System.out.println("The item begins to glow faintly.");
}
@Override
public void apply() {
System.out.println("The item flies and strikes the enemies finally returning to owner's hand.");
}
@Override
public void deactivate() {
System.out.println("The item's glow fades.");
}
}
SoulEnchantment.java
public class SoulEatingEnchantment implements Enchantment {
@Override
public void activate() {
System.out.println("The item spreads bloodlust.");
}
@Override
public void apply() {
System.out.println("The item eats the soul of enemies");
}
@Override
public void deactivate() {
System.out.println("Bloodlust slowly disappears.");
}
}
然后是RefinedAbstraction——Sword.java & Dragger.java
Sword.java
public class Sword implements Weapon {
private final Enchantment enchantment;
public Sword(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
System.out.println("The sword is wielded");
enchantment.activate();
}
@Override
public void swing() {
System.out.println("The sword is swinged");
enchantment.apply();
}
@Override
public void retract() {
System.out.println("The sword is retracted");
enchantment.deactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
Dagger.java
public class Dagger implements Weapon {
private final Enchantment enchantment;
public Dagger(Enchantment enchantment) {
this.enchantment = enchantment;
}
@Override
public void wield() {
System.out.println("The Dagger is wielded");
enchantment.activate();
}
@Override
public void swing() {
System.out.println("The Dagger is swinged");
enchantment.apply();
}
@Override
public void retract() {
System.out.println("The Dagger is retracted");
enchantment.deactivate();
}
@Override
public Enchantment getEnchantment() {
return enchantment;
}
}
接下是Clent——WeaponShop.java
public class WeaponShop {
public static void main(String[] args) {
Weapon sword = new Sword(new SoulEatingEnchantment());
sword.wield();
sword.swing();
sword.retract();
System.out.println();
Weapon dagger = new Dagger(new FlyingEnchantment());
dagger.wield();
dagger.swing();
dagger.retract();
}
}
以及对于的测试结果
The sword is wielded
The item spreads bloodlust.
The sword is swinged
The item eats the soul of enemies
The sword is retracted
Bloodlust slowly disappears.
The Dagger is wielded
The item begins to glow faintly.
The Dagger is swinged
The item flies and strikes the enemies finally returning to owner's hand.
The Dagger is retracted
The item's glow fades.
最后附上类的UML图(PS:由于这里Abstraction是接口,在RefinedAbstraction中持有Implementor引用)
NeXT’s AppKit 在图像生成和显示中使用了Bridge模式,一个图像可以有不同的表示方式,一个图像的最佳显式方式取决于显式设备的特性,特别是它的色彩数目和分辨率。如果没有AppKit的帮助,每一个应用程序中应用开发者都要确定在不同的情况下使用哪一种实现方法。
《设计模式:可复用面向对象软件的基础》
《HeadFirst设计模式》