`
gstarwd
  • 浏览: 1511130 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

桥梁(Bridge)模式

阅读更多

一、 桥梁(Bridge)模式

桥梁模式是一个非常有用的模式,也是比较复杂的一个模式。熟悉这个模式对于理解面向对象的设计原则,包括"开-闭"原则(OCP)以及组合/聚合复用原则(CARP)都很有帮助。理解好这两个原则,有助于形成正确的设计思想和培养良好的设计风格。

注:《Java与模式》一书认为Bridge模式不是一个使用频率很高的模式,我不太赞同,我认为Bridge模式中蕴涵了很多设计模式的关键思想在里面,所以我这里采纳了《Design Patterns Explained》一书的作者Alan Shalloway与James R. Trott的观点:The Bridge pattern is quite a bit more complex than the other patterns you just learned; it is also much more useful.

桥梁模式的用意

【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。

抽象化

存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待【LISKOV94】。

实现化

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

脱耦

所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。

将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。


二、 桥梁模式的结构

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

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

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

  • 由抽象化角色和修正抽象化角色组成的抽象化等级结构。
  • 由实现化角色和两个具体实现化角色所组成的实现化等级结构。

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

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
  • 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。


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

// 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");
  }

}


/**//// <summary>
/// Client test
/// </summary>

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章的对变化的封装一节也写得很不错,推荐大家读一读。它深入的阐述了《Design Patterns Explained》一书中"1)Design to interfaces. 2)Favor composition over inheritance. 3)Find what varies and encapsulate it"的三个观点。)。

如图所示,有大量的调制解调器客户程序在使用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; }
    
getreturn dataObject; }
  }


  
// Methods
  virtual public void Next()
  
{ dataObject.NextRecord(); }

  
virtual public void Prior()
  
{ dataObject.PriorRecord(); }

  
virtual public void New( string name )
  
{ dataObject.NewRecord( name ); }

  
virtual public void Delete( string name )
  
{ dataObject.DeleteRecord( name ); }

  
virtual public void Show()
  
{ dataObject.ShowRecord(); }

  
virtual public void ShowAll()
  
{
    Console.WriteLine( 
"Customer Group: {0}", group );
    dataObject.ShowAllRecords();
  }

}


// "RefinedAbstraction"
class CustomersBusinessObject : BusinessObject
{
  
// Constructors
  public CustomersBusinessObject( string group )
    : 
base( group ){}

  
// Methods
  override public void ShowAll()
  
{
    
// Add separator lines
    Console.WriteLine();
    Console.WriteLine( 
"------------------------" );
    
base.ShowAll();
    Console.WriteLine( 
"------------------------" );
  }

}


// "Implementor"
abstract class DataObject
{
  
// Methods
  abstract public void NextRecord();
  
abstract public void PriorRecord();
  
abstract public void NewRecord( string name );
  
abstract public void DeleteRecord( string name );
  
abstract public void ShowRecord();
  
abstract public void ShowAllRecords();
}


// "ConcreteImplementor"
class CustomersDataObject : DataObject
{
  
// Fields
  private ArrayList customers = new ArrayList();
  
private int current = 0;

  
// Constructors
  public CustomersDataObject()
  
{
    
// Loaded from a database
    customers.Add( "Jim Jones" );
    customers.Add( 
"Samual Jackson" );
    customers.Add( 
"Allen Good" );
    customers.Add( 
"Ann Stills" );
    customers.Add( 
"Lisa Giolani" );
  }


  
// Methods
  public override void NextRecord()
  
{
    
if( current <= customers.Count - 1 )
      current
++;
  }


  
public override void PriorRecord()
  
{
    
if( current > 0 )
      current
--;
http://gstarwd
分享到:
评论

相关推荐

    设计模式C++学习之桥梁模式(Bridge)

    **桥梁模式(Bridge)**是一种结构型设计模式,它的主要目的是将抽象部分与其实现部分解耦,使得它们可以独立地进行变化。在C++编程中,这种模式尤其有用,因为它允许我们创建灵活且可扩展的系统,同时保持代码的低...

    Bridge模式

    在Bridge模式中,抽象类(Abstract Class)和实现类(Implementor Class)是两个独立的继承体系,通过一个“桥梁”类(Bridge Class)进行连接,使得抽象部分可以动态地更换不同的实现部分,实现部分也可以独立地...

    (结构型模式)Bridge模式

    Bridge模式通过引入一个抽象接口作为桥梁,使得抽象类与其实现类能够解耦,提供更大的灵活性。 在软件开发中,随着系统的复杂性增加,抽象类和它的实现类可能会变得越来越庞大,导致代码难以维护。Bridge模式通过将...

    设计模式之桥梁模式实现

    桥梁模式(Bridge Pattern)是设计模式中的一种结构型模式,它的核心思想是将抽象部分与实现部分分离,使得它们可以独立地进行变化。在这个主题中,我们将深入探讨桥梁模式的原理、结构以及如何在Java中实现。 首先...

    设计模式之桥梁模式

    **桥梁模式(Bridge Pattern)详解** 桥梁模式是一种结构型设计模式,它将抽象部分与实现部分分离,使得它们可以独立地进行变化。这种模式的主要目的是为了将抽象化和实现化解耦,使得两者可以独立发展,增加了系统...

    桥梁模式实例

    桥梁模式是一种设计模式,它是结构型模式的一种,其主要目的是将抽象部分与实现部分分离,使得它们可以独立地进行变化。在Java中实现桥梁模式,我们可以有效地解耦系统中的类,提高代码的可扩展性和可维护性。 首先...

    学习php设计模式 php实现桥梁模式(bridge)

    【PHP设计模式】桥梁模式(Bridge Pattern)是一种结构型设计模式,它的主要目的是将抽象部分与其实现部分解耦,使两者能够独立地进行变化。在PHP中,桥梁模式可以帮助我们构建更灵活、可扩展的系统,降低组件间的...

    桥梁模式DEMO

    1. **模式定义**:桥梁模式(Bridge Pattern)是一种行为设计模式,它将抽象部分与实现部分分离,使得它们可以独立地进行变化。这种模式的主要目标是将抽象部分和实现部分解耦,这样即使两部分都需要改变,也可以...

    Java24种设计模式,Java24种设计模式,24种设计模式,学会了这24种设计模式,可以打遍天下无敌手,设计模式非常重要

    11、桥梁模式BRIDGE PATTERN 12、命令模式COMMAND PATTERN 13、装饰模式DECORATOR PATTERN 14、迭代器模式ITERATOR PATTERN 15、组合模式COMPOSITE PATTERN 16、观察者模式OBSERVER PATTERN 17、责任链模式 18、...

    JAVA桥梁模式.docx

    **桥梁模式(Bridge Pattern)**是一种设计模式,属于对象的结构模式,主要目的是为了将抽象部分与其实现部分分离,使这两者可以独立地进行变化。这种模式也被称为柄体模式或接口模式,它有助于实现开闭原则,即对扩展...

    JavaScript设计模式–桥梁模式引入操作实例分析

    JavaScript设计模式中的桥梁模式是一种结构型模式,它旨在将抽象部分与实现部分分离,使得它们可以独立地进行变化。在本文中,通过几个实例来解释如何在JavaScript中应用桥梁模式。 首先,我们来看一个基本的事件...

    Bridge 桥接

    在传统的继承关系中,抽象类和具体实现通常是紧密绑定的,而桥接模式通过引入一个“桥梁”角色,使得两者可以独立发展。这提供了更大的灵活性,允许在运行时动态地改变对象的行为。 在IT领域,桥接模式常用于处理...

    b4a_bridge.apk.rar

    - **连接建立**:b4a_bridge.apk的主要作用是在Android手机与开发者的电脑之间建立桥梁,允许两者通过网络进行通信,实现远程调试。 - **调试接口**:这个程序可能包含了必要的API和协议,使得B4A IDE可以发送调试...

    Adapter(适配器)模式[定义].pdf

    在Bridge模式中,抽象部分和实现部分通过一个“桥梁”类关联,这样即使需求变更,也不需要修改原有的代码结构,而是通过增加新的实现类或抽象类来适应变化。这有助于减少类的膨胀,提高代码的可维护性和可复用性。 ...

    您的设计模式.pdf。

    桥梁模式(BRIDGE PATTERN) 桥梁模式是一种结构型设计模式,它将一个类的抽象部分与实现部分分离,以便可以独立地更改它们。桥梁模式可以帮助开发者编写更加灵活的代码,以提高系统的可维护性。例如,在一个游戏中...

    bridge-example:马尔马拉大学 OOP 课堂作业 - 桥梁设计模式示例

    **桥梁设计模式(Bridge Pattern)详解** 在软件工程中,桥梁设计模式是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们可以独立地进行变化。这个概念源于对象的多态性,旨在降低类之间的耦合度,提高...

    GOF中桥梁模式的java实现,及说明.pdf

    桥接模式(Bridge Pattern)是一种设计模式,主要用于将抽象部分(Abstraction)与实现部分(Implementation)解耦,以便两者能够独立地进行变化。这种模式是对象的结构模式,它通过引入一个抽象层来分离抽象和实现...

    JavaScript设计模式.pdf

    3.桥梁模式(Bridge Pattern): 桥梁模式是将抽象部分与实现部分分离,使他们可以独立地变化。它的主要作用是降低耦合度,提高代码的灵活性和可扩展性。 4. 装饰者模式(Decorator Pattern): 装饰者模式是动态地...

    rosbridge_suite-develop.zip

    ROSBridge的主要功能是通过JSON消息格式在ROS和非ROS环境之间建立桥梁,使得数据能够在两者间无缝传输。Python端的代码主要负责解析和构造这些JSON消息,以便在ROS的Topic、Service和Action框架下工作。以下是一些...

Global site tag (gtag.js) - Google Analytics