您的位置:首页 > 其它

桥梁模式(Bridge Pattern,对象结构型模式)

2017-07-24 00:00 232 查看

意图

功能的类层次和实现的类层次
将抽象部分和它的实现部分分离,使它们都可以独立变化。(抽象部分并不是简单的实现与继承而是一种组合关系)实现部分是被抽象部分调用,以用来完成(实现)抽象部分的功能
将抽象化(多个实体中的共性概念)和实现化(抽象化的具体实现)解耦,使得二者可以独立变化。
分层功能层和实现层
强关系:编译期确定,在运行期无法动态改变,如继承
弱关系:运行期可以动态改变,如聚合
不变的部分抽象定义,变化的部分采用子类实现
总结:提取不变的信息,作为抽象角色,把变化的信息使用具体子类实现,并且为这些子类提供统一的接口方便抽象角色应用。
两个维度排列组合。

适用性

以下一些情况使用Bridge模式:
1. 你不希望在抽象和它的实现部分之间有一个固定的绑架关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
2. 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
3. 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译
4. (C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。
5. 你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
6. 当有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。“嵌套的普化”。
7. 有多个地方要使用到类的行为,或则是多个类似行为的组合,或则行为要被不同相似类使用
8. 系统中某个类的行为可能会有几种不同的变化趋势

结构



参与者

Abstraction

定义抽象类的接口

维护一个指向Implementor类型对象的指针

RefinedAbstraction

扩充由Abstraction定义的接口

Implementor

定义实现类的接口,该接口不一定要与Abstraction的接口完全一致;事实上这两个接口可以完全不同。一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作。

ConcreteImplementor

实现Implementor接口并定义它的具体实现。

代码

Abstraction

public abstract class Abstraction {
private Implementor implementor;
public Abstraction(Implementor implementor){this.implementor = implementor;}
public abstract void clientMethod();
public Implementor getImplementor() {
return implementor;
}
public void setImplementor(Implementor implementor) {
this.implementor = implementor;
}
}

RefinedAbstraction

public class RefinedAbstraction1 extends Abstraction{
public RefinedAbstraction1(Implementor implementor) {
super(implementor);
}
public void clientMethod() {
System.out.println("====RefinedAbstraction1======clientMethod=");
this.getImplementor().method1();
this.getImplementor().method2();
this.getImplementor().method3();
this.getImplementor().method4();
}
}
public class RefinedAbstraction2 extends Abstraction{
public RefinedAbstraction2(Implementor implementor) {
super(implementor);
}
public void clientMethod() {
System.out.println("====RefinedAbstraction2======clientMethod=");
this.getImplementor().method1();
this.getImplementor().method2();
this.getImplementor().method3();
this.getImplementor().method4();
}
}

Implementor

public interface Implementor {
public void method1();
public void method2();
public void method3();
public void method4();
}

ConcreteImplementor

public class ConcreteImplementor implements Implementor{
public void method1() {
System.out.println("====ConcreteImplementor=====method1==");
}
public void method2() {
System.out.println("====ConcreteImplementor=====method2==");
}
public void method3() {
System.out.println("====ConcreteImplementor=====method3==");
}
public void method4() {
System.out.println("====ConcreteImplementor=====method4==");
}
}
public class ConcreteImplementor2 implements Implementor{
public void method1() {
System.out.println("====ConcreteImplementor2=====method1==");
}
public void method2() {
System.out.println("====ConcreteImplementor2=====method2==");
}
public void method3() {
System.out.println("====ConcreteImplementor2=====method3==");
}
public void method4() {
System.out.println("====ConcreteImplementor2=====method4==");
}
}

Client

public class Client {
public static void main(String[] args) {
Abstraction a1 = new RefinedAbstraction1(new ConcreteImplementor());
a1.clientMethod();
}
}

协作

Abstraction将client的请求转发给它的Implementor对象。

效果

分离接口及其实现部分

一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。
将Abstraction与Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类时,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。
另外,接口和实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abstraction和Implementor即可。

提高可扩充性

你可以独立地对Abstraction和Implementor层次结构进行扩充。

实现细节对客户端透明

你可以对客户隐藏实现细节,例如共享Implementor对象以及相应的引用计数机制(如果有的话)。

缺点

1.增加了复杂度,子类的扩展会发生爆炸增长

实现

仅有一个Implementor

在仅有一个实现的时候,没有必要创建一个抽象的Implementor类。这是Bridge模式的退化情况;在Abstraction与Implementor之间有一种一对一关系。尽管如此,当你希望改变一个类的实现不会影响已有的客户程序时,模式的分离机制还是非常有用的,也就是说,不必重新编译它们,仅需重新连接即可。
在C++中,Implementor类的类接口可以在一个私有的头文件中定义,这个文件不提供给客户。这样你就对客户彻底隐藏了一个类的实现部分。

创建正确的Implementor对象

如果Abstraction知道所有的ConcreteImplementor类,它就可以在它的构造器中对其中一个类进行实例化,它可以通过传递给构造器的参数确定实例化哪一个类。例如,如果一个collection类支持多重实现,既可以根据collection的大小决定实例化哪一个类。链表的实现可以用于较小的collection,而hash表则可用于较大的collection类。

另外一种方法是首先选择一个缺省的实现,然后根据需要改变这个实现。例如,如果一个collection的大小超出了一定的阀值时,它将会切换它的实现,使之更适合用于表目较多的collection。

也可以代理给另一个对象,由它一次决定。使用Abstract Factory实现封装,这种方法的优点是Abstract类不和任何一个Implementor类直接耦合。

共享Implementor

采用多重继承机制

在C + +中可以使用多重继承机制将抽象接口和它的实现部分结合起来。例如,一个类可以用public方式继承Abstraction而以 private方式继承ConcreteImplementor。但是由于这种方法依赖于静态继承,它将实现部分与接口固定不变的绑定在一起。因此不可能使用多重继承的方法实现真正的Bridge模式—至少用C + +不行

经典例子

跨平台的软件,不同电视机和不同的遥控器,java awt(不同的系统软件界面不同)、驱动程序(JDBC驱动器)
你可以分析变化的种类,将不变的框架使用抽象类定义出来,然后在将变变化的内容使用具体的子类分别实现。这样面向客户的只是一个抽象类,这种方式可以较好的避免为抽象类中现有的接口添加新的实现所带来的影响,缩小了变化带来的影响,但是这可能会造成子类数量的爆炸,并且在某些时候不是很灵活。

相关模式

Template Method Pattern

Template Method Pattern是利用实现的类层次,在父类使用抽象方法设计程序,在子类中具体实现。

Abstract Factory Pattern

抽象工厂模式可以用来创建和配置一个特定的Bridge模式。为了让Bridge Pattern中的ConcreteImplementor参与者设计的更配合环境条件,有时会用到Abstract Factory Pattern。

Adapter Pattern

适配器模式用来帮助无关的类协同工作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。Bridge Pattern是把功能的类层次和实现的类层次两者区分清楚后使用的Pattern;Adapter Pattern的功能很类似,但接口(API)是结合不同类的Pattern。

敬请期待“适配器模式(Adapter Pattern 类对象结构型模式)”
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: