桥梁模式的结构与示意性源代码.docx

上传人:b****4 文档编号:1375610 上传时间:2022-10-21 格式:DOCX 页数:12 大小:67.84KB
下载 相关 举报
桥梁模式的结构与示意性源代码.docx_第1页
第1页 / 共12页
桥梁模式的结构与示意性源代码.docx_第2页
第2页 / 共12页
桥梁模式的结构与示意性源代码.docx_第3页
第3页 / 共12页
桥梁模式的结构与示意性源代码.docx_第4页
第4页 / 共12页
桥梁模式的结构与示意性源代码.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

桥梁模式的结构与示意性源代码.docx

《桥梁模式的结构与示意性源代码.docx》由会员分享,可在线阅读,更多相关《桥梁模式的结构与示意性源代码.docx(12页珍藏版)》请在冰豆网上搜索。

桥梁模式的结构与示意性源代码.docx

桥梁模式的结构与示意性源代码

一、 桥梁(Bridge)模式

桥梁模式是一个非常有用的模式,也是比较复杂的一个模式。

熟悉这个模式对于理解面向对象的设计原则,包括"开-闭"原则(OCP)以及组合/聚合复用原则(CARP)都很有帮助。

理解好这两个原则,有助于形成正确的设计思想和培养良好的设计风格。

桥梁模式的用意

【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。

这句话有三个关键词,也就是抽象化、实现化和脱耦。

抽象化

存在于多个实体中的共同的概念性联系,就是抽象化。

作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待【LISKOV94】。

实现化

抽象化给出的具体实现,就是实现化。

脱耦

所谓耦合,就是两个实体的行为的某种强关联。

而将它们的强关联去掉,就是耦合的解脱,或称脱耦。

在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。

将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。

因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。

这就是桥梁模式的用意。

二、 桥梁模式的结构

桥梁模式【GOF95】是对象的结构模式,又称为柄体(HandleandBody)模式或接口(Interface)模式。

下图所示就是一个实现了桥梁模式的示意性系统的结构图。

可以看出,这个系统含有两个等级结构,也就是:

∙由抽象化角色和修正抽象化角色组成的抽象化等级结构。

∙由实现化角色和两个具体实现化角色所组成的实现化等级结构。

桥梁模式所涉及的角色有:

∙抽象化(Abstraction)角色:

抽象化给出的定义,并保存一个对实现化对象的引用。

∙修正抽象化(RefinedAbstraction)角色:

扩展抽象化角色,改变和修正父类对抽象化的定义。

∙实现化(Implementor)角色:

这个角色给出实现化角色的接口,但不给出具体的实现。

必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。

实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。

∙具体实现化(ConcreteImplementor)角色:

这个角色给出实现化角色接口的具体实现。

三、 桥梁模式的示意性源代码

 

// Bridge pattern -- Structural example  

using System;

// "Abstraction"

class Abstraction

{

  // Fields

  protected Implementor implementor;

  // Properties

  public Implementor Implementor

  {

    set{ implementor = value; }

  }

  // Methods

  virtual public void Operation()

  {

    implementor.Operation();

  }

}

// "Implementor"

abstract class Implementor

{

  // Methods

  abstract public void Operation();

}

// "RefinedAbstraction"

class RefinedAbstraction :

 Abstraction

{

  // Methods

  override public void Operation()

  {

    implementor.Operation();

  }

}

// "ConcreteImplementorA"

class ConcreteImplementorA :

 Implementor

{

  // Methods

  override public void Operation()

  {

    Console.WriteLine("ConcreteImplementorA Operation");

  }

}

// "ConcreteImplementorB"

class ConcreteImplementorB :

 Implementor

{

  // Methods

  override public void Operation()

  {

    Console.WriteLine("ConcreteImplementorB Operation");

  }

}

/**//// 

/// Client test

/// 

public class Client

{

  public static void Main( string[] args )

  {

    Abstraction abstraction = new RefinedAbstraction();

    // Set implementation and call

    abstraction.Implementor = new ConcreteImplementorA();

    abstraction.Operation();

    // Change implemention and call

    abstraction.Implementor = new ConcreteImplementorB();

    abstraction.Operation();

  }

}

 

四、 调制解调器问题

感觉《敏捷软件开发-原则、模式与实践》中关于Bridge模式的例子很好。

(《Java与模式》一书33章的对变化的封装一节也写得很不错,推荐大家读一读。

它深入的阐述了《DesignPatternsExplained》一书中"1)Designtointerfaces.2)Favorcompositionoverinheritance.3)Findwhatvariesandencapsulateit"的三个观点。

)。

如图所示,有大量的调制解调器客户程序在使用Modem接口。

Modem接口被几个派生类HayesModem、USRoboticsModem和EarniesModem实现。

它很好地遵循了OCP、LSP和DIP。

当增加新种类的调制解调器时,调制解调器的客户程序不会受影响。

假定这种情形持续了几年,并有许多调制解调器的客户程序都在使用着Modem接口。

现出现了一种不拨号的调制解调器,被称为专用调制解调器。

它们位于一条专用连接的两端。

有几个新应用程序使用这些专用调制解调器,它们无需拨号。

我们称这些使用者为DedUser。

但是,客户希望当前所有的调制解调器客户程序都可以使用这些专用调制解调器。

他们不希望去更改许许多多的调制解调器客户应用程序,所以完全可以让这些调制解调器客户程序去拨一些假(dummy)电话号码。

如果能选择的话,我们会把系统的设计更改为下图所示的那样。

我们把拨号和通信功能分离为两个不同的接口。

原来的调制解调器实现这两个接口,而调制解调器客户程序使用这两个接口。

DedUser只使用Modem接口,而DedicateModem只实现Modem接口。

但这样做会要求我们更改所有的调制解调器客户程序--这是客户不允许的。

一个可能的解决方案是让DedicatedModem从Modem派生并且把dial方法和hangup方法实现为空,就像下面这样:

几个月后,已经有了大量的DedUser,此时客户提出了一个新的更改。

为了能拨国际电话号码、信用卡电话、PIN标识电话等等,必修对现有dial中使用char[10]存储号码改为能够拨打任意长度的电话号码。

显然,所有的调制解调器客户程序都必须更改。

客户同意了对调制解调器客户程序的更改,因为他们别无选择。

糟糕的是,现在必须要去告诉DedUser的编写者,他们必须要更改他们的代码!

你可以想象他们听到这个会有多高兴。

本来他们是不用调用dial的。

这就是许多项目都会具有的那种有害的混乱依赖关系。

系统某一部分中的一个杂凑体(kludge)创建了一个有害的依赖关系,最终导致系统中完全无关的部分出现问题。

如果使用ADAPTER模式解决最初的问题的话,就可以避免这个严重问题。

如图:

请注意,杂凑体仍然存在。

适配器仍然要模拟连接状态。

然而,所有的依赖关系都是从适配器发起的。

杂凑体和系统隔离,藏身于几乎无人知晓的适配器中。

BRIDGE模式

看待这个问题,还有另外一个方式。

现在,出现了另外一种切分Modem层次结构的方式。

如下图:

这不是一个理想的结构。

每当增加一款新硬件时,就必须创建两个新类--一个针对专用的情况,一个针对拨号的情况。

每当增加一种新连接类型时,就必须创建3个新类,分别对应3款不同的硬件。

如果这两个自由度根本就是不稳定的,那么不用多久,就会出现大量的派生类。

在类型层次结构具有多个自由度的情况中,BRIDGE模式通常是有用的。

我们可以把这些层次结构分开并通过桥把它们结合到一起,而不是把它们合并起来。

如图:

我们把调制解调器类层次结构分成两个层次结构。

一个表示连接方法,另一个表示硬件。

这个结构虽然复杂,但是很有趣。

它的创建不会影响到调制解调器的使用者,并且还完全分离了连接策略和硬件实现。

ModemConnectController的每个派生类代表了一个新的连接策略。

在这个策略的实现中可以使用sendlmp、receivelmp、diallmp和hanglmp。

新imp方法的增加不会影响到使用者。

可以使用ISP来给连接控制类增加新的接口。

这种做法可以创建出一条迁移路径,调制解调器的客户程序可以沿着这条路径慢慢地得到一个比dial和hangup层次更高的API。

五、 另外一个实际应用Bridge模式的例子

该例子演示了业务对象(BusinessObject)通过Bridge模式与数据对象(DataObject)解耦。

数据对象的实现可以在不改变客户端代码的情况下动态进行更换。

 

// Bridge pattern -- Real World example

using System;

using System.Collections;

// "Abstraction"

class BusinessObject

{

  // Fields

  private DataObject dataObject;

  protected string group;

  // Constructors

  public BusinessObject( string group )

  {

    this.group = group;

  }

  // Properties

  public DataObject DataObject

  {

    set{ dataObject = value; }

    get{ return dataObject; }

  }

  // Methods

  virtual public void Next()

  { dataObject.NextRecord(); }

  virtual public void Prior()

  { dataObject.P

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 水产渔业

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1