`
qzriso
  • 浏览: 242643 次
  • 性别: Icon_minigender_1
  • 来自: ph
社区版块
存档分类
最新评论

设计模式之桥接模式

阅读更多

概述 软件开发网 www.mscto.com

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用 Bridge 模式。

意图

将抽象部分与实现部分分离,使它们都可以独立的变化。 [GOF 《设计模式》 ]

结构图

1 Bridge 模式结构图

生活中的例子

桥接模式将抽象部分与它的实现分离,使它们能够独立地变化。一个普通的开关控制的电灯、电风扇等等,都是桥接的例子。开关的目的是将设备打开或关闭。实际的开关可以是简单的双刀拉链开关,也可以是调光开关。

软件开发网 www.mscto.com

2 使用电子开关例子的桥接对象图

桥接模式解说

在创建型模式里面,我曾经提到过抽象与实现,抽象不应该依赖于具体实现细节,实现细节应该依赖于抽象。看下面这幅图:

3   抽象不应该依赖于实现细节

在这种情况下,如果抽象 B 稳定,而实现细节 b 变化,这时用创建型模式来解决没有问题。但是如果抽象 B 也不稳定,也是变化的,该如何解决?这就要用到 Bridge 模式了。

我们仍然用日志记录工具这个例子来说明 Bridge 模式。现在我们要开发一个通用的日志记录工具,它支持数据库记录 DatabaseLog 和文本文件记录 FileLog 两种方式,同时它既可以运行在 .NET 平台,也可以运行在 Java 平台上。

根据我们的设计经验,应该把不同的日志记录方式分别作为单独的对象来对待,并为日志记录类抽象出一个基类 Log 出来,各种不同的日志记录方式都继承于该基类:

软件开发网 www.mscto.com

4 Log 类结构图

实现代码如下:

public abstract class Log 软件开发网 www.mscto.com

{

    public abstract void Write(string log);

}

软件开发网 www.mscto.com

public class DatabaseLog : Log

{

    public override void Write(string log)

    {

        //......Log Database

    }

软件开发网 www.mscto.com

}

 

public class TextFileLog : Log

{

    public override void Write(string log)

    {

       //......Log Text File

    }

}

软件开发网 www.mscto.com

另外考虑到不同平台的日志记录,对于操作数据库、写入文本文件所调用的方式可能是不一样的,为此对于不同的日志记录方式,我们需要提供各种不同平台上的实现,对上面的类做进一步的设计得到了下面的结构图:

软件开发网 www.mscto.com

5

实现代码如下:

public class NDatabaseLog : DatabaseLog

{

    public override void Write(string log)

    {

        //......(.NET 平台)Log Database

    }

}

 

public class JDatabaseLog : DatabaseLog

{

    public override void Write(string log)

    {

        //......(Java 平台)Log Database

    }

} 软件开发网 www.mscto.com

 

public class NTextFileLog : TextFileLog

{

    public override void Write(string log)

    {

        //......(.NET 平台)Log Text File

    }

}

 

public class JTextFileLog : TextFileLog

{

    public override void Write(string log)

    {

        //......(Java 平台)Log TextFile

    }

}

现在的这种设计方案本身是没有任何错误的,假如现在我们要引入一种新的 xml 文件的记录方式,则上面的类结构图会变成:

6

如图中蓝色的部分所示,我们新增加了一个继承于 Log 基类的子类,而没有修改其它的子类,这样也符合了开放 - 封闭原则。如果我们引入一种新的平台,比如说我们现在开发的日志记录工具还需要支持 Borland 平台,此时该类结构又变成了:

7

同样我们没有修改任何的东西,只是增加了两个继承于 DatabaseLog TextFileLog 的子类,这也符合了开放 - 封闭原则。

但是我们说这样的设计是脆弱的,仔细分析就可以发现,它还是存在很多问题,首先它在遵循开放 - 封闭原则的同时,违背了类的单一职责原则,即一个类只有一个引起它变化的原因,而这里引起 Log 类 变化的原因却有两个,即日志记录方式的变化和日志记录平台的变化;其次是重复代码会很多,不同的日志记录方式在不同的平台上也会有一部分的代码是相同的; 再次是类的结构过于复杂,继承关系太多,难于维护,最后最致命的一点是扩展性太差。上面我们分析的变化只是沿着某一个方向,如果变化沿着日志记录方式和不 同的运行平台两个方向变化,我们会看到这个类的结构会迅速的变庞大。

现在该是 Bridge 模式粉墨登场的时候了,我们需要解耦这两个方向的变化,把它们之间的强耦合关系改成弱联系。我们把日志记录方式和不同平台上的实现分别当作两个独立的部分来对待,对于日志记录方式,类结构图仍然是:

8

现在我们引入另外一个抽象类 ImpLog ,它是日志记录在不同平台的实现的基类,结构图如下:

9

实现代码如下:

public abstract class ImpLog

{

    public abstract void Execute(string msg);

}

 

public class NImpLog : ImpLog

{

    public override void Execute(string msg)

    {

        //...... .NET 平台

    }

}

软件开发网 www.mscto.com

 

public class JImpLog : ImpLog

{

    public override void Execute(string msg)

    {

        //...... Java 平台

    }

}

这时对于日志记录方式和不同的运行平台这两个类都可以独立的变化了,我们要做的工作就是把这两部分之间连接起来。那如何连接呢?在这里, Bridge 使用了对象组合的方式,类结构图如下:

10

实现代码如下:
public abstract class Log 软件开发网 www.mscto.com

{

    protected ImpLog implementor;

软件开发网 www.mscto.com

 

    public ImpLog Implementor

软件开发网 www.mscto.com

    {

        set { implementor = value ; }   

软件开发网 www.mscto.com

    }

 

    public virtual void Write(string log)

    {

        implementor.Execute(log);

    }

}

软件开发网 www.mscto.com

public class DatabaseLog : Log

{

    public override void Write(string log)

    {

        implementor.Execute(log);

    }

}

 

public class TextFileLog : Log

{

软件开发网 www.mscto.com

    public override void Write(string log)

    {

        implementor.Execute(log);

    } 软件开发网 www.mscto.com

}

可以看到,通过对象组合的方式, Bridge 模式把两个角色之间的继承关系改为了耦合的关系,从而使这两者可以从容自若的各自独立的变化,这也是 Bridge 模式的本意。再来看一下客户端如何去使用:

class App

{

    public static void Main(string [] args)

    {

        //.NET 平台下的Database Log

        Log dblog = new DatabaseLog ();

        dblog.Implementor = new NImpLog ();

        dblog.Write();

       

软件开发网 www.mscto.com

        //Java 平台下的Text File Log

        Log txtlog = new TextFileLog ();

        txtlog.Implementor = new JImpLog ();

        txtlog.Write();

    }

} 软件开发网 www.mscto.com

可能有人会担心说,这样不就又增加了客户程序与具体日志记录方式之间的耦合性了吗?其实这样的担心是没有必要的,因为这种耦合性是由于对象的创建所带来的,完全可以用创建型模式去解决,就不是这里我们所讨论的内容了。

最后我们再来考虑一个问题,为什么 Bridge 模式要使用对象组合的方式而不是用继承呢?如果采用继承的方式,则 Log 类, ImpLog 类都为接口,类结构图如下:

11

实现代码如下:

public class NDatabaseLog : DatabaseLog , IImpLog

{

    //......

}

public class JDatabaseLog : DatabaseLog , IImpLog

{

    //......

}

public class NTextFileLog : TextFileLog , IImpLog 软件开发网 www.mscto.com

{

     //......

} 软件开发网 www.mscto.com

public class JTextFileLog : TextFileLog , IImpLog

{

软件开发网 www.mscto.com

    //......

}

如上图中蓝色的部分所示,它们既具有日志记录方式的特性,也具有接口 IimpLog 的特性,它已经违背了面向对象设计原则中类的单一职责原则,一个类应当仅有一个引起它变化的原因。所以采用 Bridge 模式往往是比采用多继承更好的方案。说到这里,大家应该对 Bridge 模式有一些认识了吧?如果在开发中遇到有两个方向上纵横交错的变化时,应该能够想到使用 Bridge 模式,当然了,有时候虽然有两个方向上的变化,但是在某一个方向上的变化并不是很剧烈的时候,并不一定要使用 Bridge 模式。

软件开发网 www.mscto.com

效果及实现要点

1 Bridge 模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。

2 .所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同平台上的不同型号。

3 Bridge 模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。 Bridge 模式是比多继承方案更好的解决方法。

4 Bridge 模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用 Bridge 模式。

适用性

在以下的情况下应当使用桥梁模式:

1 .如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。

2 .设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。

3 .一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。

4 .虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。

总结

Bridge 模式是一个非常有用的模式,也非常复杂,它很好的符合了开放 - 封闭原则和优先使用对象,而不是继承这两个面向对象原则。

分享到:
评论

相关推荐

    设计模式之桥接模式.pdf

    ### 设计模式之桥接模式详解 #### 一、桥接模式概述 桥接模式(Bridge Pattern)是一种常用的结构型设计模式,它主要用于解决抽象部分和实现部分的耦合问题。这种模式通过将抽象和实现分离,使得两者可以独立变化...

    设计模式之桥接模式,内含可运行代码

    桥接模式是软件设计模式中的一种结构型模式,它的主要目的是为了实现抽象和实现的解耦,使得两者可以独立地进行扩展。在桥接模式中,抽象类(Abstraction)不直接引用具体实现(Implementation),而是通过一个桥接...

    设计模式之桥接模式BridgePattern

    桥接模式(Bridge Pattern)是设计模式中的一种结构型模式,它主要解决的是在软件设计中,当抽象和实现之间存在紧密耦合时,如何使这两者能够独立地变化。这种模式通过引入一个抽象层来分离接口和实现,使得它们可以...

    c++-设计模式之桥接模式(Bridge Pattern)

    桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使得两者可以独立地变化。这种模式常用于需要在多个维度上变化的场景,比如不同的形状和颜色,允许在不改变客户端代码的情况下增加新...

    23钟设计模式之 桥接模式

    桥接模式(Bridge Pattern)是设计模式中的一种结构型模式,它的主要目的是将抽象部分与实现部分解耦,使得它们可以独立地进行变化。在上述的例子中,通过将电视台(CCTV)与节目(Program)进行分离,实现了两者...

    C++设计模式之桥接模式(Bridge)

    C++设计模式之桥接模式(Bridge) 桥接模式(Bridge)是一种结构型设计模式,它的主要作用是将抽象部分与实现部分分离,使它们可以独立地变化。这使得系统更加灵活、可扩展和易于维护。 桥接模式的定义 桥接模式...

    深入理解JavaScript系列(44):设计模式之桥接模式详解

    桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。 正文 桥接模式最常用在事件监控上,先看一段代码: 代码如下: addEvent(element, ‘click’, getBeerById); function getBeerById(e) {...

    Python设计模式之桥接模式原理与用法实例分析

    本文实例讲述了Python设计模式之桥接模式原理与用法。分享给大家供大家参考,具体如下: 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化. 下面是一个桥接模式的demo: #!/usr/bin...

    设计模式的桥接模式的例子

    桥接模式是设计模式中的一种结构型模式,它旨在将抽象部分与实现部分解耦,使得它们可以独立地变化。这种模式将抽象类和它的实现类进行分离,通过一个抽象接口来连接它们,使得两者可以独立发展,增加了系统的灵活性...

    Java设计模式之桥接模式实例详解

    Java设计模式之桥接模式实例详解 桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,...

    设计模式 - 桥接模式

    桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立进行变化。这种模式在软件工程中被广泛应用于处理组件之间的耦合问题,使得系统具有更好的可扩展性和灵活性。 桥接模式的主要组成部分...

    研磨设计模式之桥接模式

    来写一个大家既陌生又熟悉的设计模式,也是非常实用的一个设计模式,那就是桥接模式。说陌生是很多朋友并不熟悉这个设计模式,说熟悉是很多人经常见到或者是下意识的用到这个设计模式,只是不知道罢了。桥接模式是...

    设计模式11桥接模式

    ### 设计模式11桥接模式 #### 模式动机 桥接模式是解决系统中存在多维度变化问题的一种有效手段。例如,在图形绘制场景中,假设需要绘制不同形状(如矩形、圆形、椭圆、正方形)并赋予它们不同的颜色(如红色、...

Global site tag (gtag.js) - Google Analytics