.NET设计模式(9):桥接模式(Bridge Pattern)

 

桥接模式(Bridge Pattern

——.NET设计模式系列之九

Terrylee20062

概述

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

意图

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

结构图

1 Bridge模式结构图

生活中的例子

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

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

桥接模式解说

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

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

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

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

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

4 Log类结构图

实现代码如下:

public abstract class Log

{

    public abstract void Write(string log);

}

public class DatabaseLog : Log

{

    public override void Write(string log)

    {

        //......Log Database

    }

}

public class TextFileLog : Log

{

    public override void Write(string log)

    {

       //......Log Text File

    }

}

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

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

    }

}

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

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

但是我们说这样的设计是脆弱的,仔细分析就可以发现,它还是存在很多问题,首先它在遵循开放-封闭原则的同时,违背了类的单一职责原则,即一个类只有一个引起它变化的原因,而这里引起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平台

    }

}

public class JImpLog : ImpLog

{

    public override void Execute(string msg)

    {

        //...... Java平台

    }

}

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

10

实现代码如下:
public abstract class Log

{

    protected ImpLog implementor;

 

    public ImpLog Implementor

    {

        set { implementor = value; }   

    }

    public virtual void Write(string log)

    {

        implementor.Execute(log);

    }

}

public class DatabaseLog : Log

{

    public override void Write(string log)

    {

        implementor.Execute(log);

    }

}

public class TextFileLog : Log

{

    public override void Write(string log)

    {

        implementor.Execute(log);

    }

}

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

class App

{

    public static void Main(string[] args)

    {

        //.NET平台下的Database Log

        Log dblog = new DatabaseLog();

        dblog.Implementor = new NImpLog();

        dblog.Write();

       

        //Java平台下的Text File Log

        Log txtlog = new TextFileLog();

        txtlog.Implementor = new JImpLog();

        txtlog.Write();

    }

}

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

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

11

实现代码如下:

public class NDatabaseLog : DatabaseLog, IImpLog

{

    //......

}

public class JDatabaseLog : DatabaseLog, IImpLog

{

    //......

}

public class NTextFileLog : TextFileLog, IImpLog

{

    //......

}

public class JTextFileLog : TextFileLog, IImpLog

{

    //......

}

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

效果及实现要点

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

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

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

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

适用性

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

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

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

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

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

总结

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

参考资料

阎宏,《Java与模式》,电子工业出版社

James W. Cooper,《C#设计模式》,电子工业出版社

Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

MSDN WebCast C#面向对象设计模式纵横谈(8)Bridge桥接模式(结构型模式)

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-02-24 09:06 TerryLee 阅读(32347) 评论(79) 编辑 收藏

 回复 引用   
#1楼 2006-02-24 10:03 ZO[未注册用户]
分析的很透彻!!!
学习中……

 回复 引用 查看   
#2楼 2006-02-24 10:16 源码工作室      
越来越佩服Terrylee兄了。
有空想和你聊聊,我想有共同兴趣的人肯定能聊的很投机的。
很想教你这个朋友。

 回复 引用 查看   
#3楼[楼主] 2006-02-24 10:25 Terrylee      
@源码工作室
呵呵,我也很想交你这个朋友
我的MSN是lhj_cauc@hotmail.com
有机会我们多多交流~~~~

 回复 引用 查看   
#4楼 2006-02-24 12:02 quitgame      
很不错,受益匪浅

但是 个人觉得没有能看到桥的存在,桥应该在抽象类上或者第三个类上.

 回复 引用 查看   
#5楼[楼主] 2006-02-24 12:39 Terrylee      
@quitgame
可能我在表述的过程中还有些地方没有点透,说得不够清楚~~~

 回复 引用 查看   
#6楼 2006-02-24 14:27 A.Z      
请看httpwebrequest
 回复 引用 查看   
#7楼[楼主] 2006-02-24 15:41 Terrylee      
@A.Z
HttpWebRequest中使用了桥接模式了吗?
我只知道它里面应用了工厂方法模式。
如果你有这方面的资料,不妨介绍一下,共同学习!

 回复 引用   
#8楼 2006-02-25 10:39 M[未注册用户]
能把设计模式分析的如此透彻的文章少见,期待……
 回复 引用   
#9楼 2006-02-25 22:47 劲舞团[未注册用户]
不错。。。收藏了。。。。
 回复 引用 查看   
#10楼 2006-02-27 09:24 开心糊涂      
谢谢分享
 回复 引用   
#11楼 2006-02-28 15:37 mrhgw[未注册用户]

有一点疑问,就以上面的例子:
方式一:
Log dblog = new DatabaseLog();
dblog.Implementor = new NImpLog();
dblog.Write();

方式二:
Log dblog = new DatabaseLog();
dblog.Implementor = new JImpLog();
dblog.Write();

方式三:
//Java平台下的Text File Log
Log txtlog = new TextFileLog();
txtlog.Implementor = new JImpLog();
txtlog.Write();

方式四:
Log txtlog = new TextFileLog();
txtlog.Implementor = new NImpLog();
txtlog.Write();

从实际调用来说(我的理解),方法一和方法四应该是调用同一个方法;方法二和方法三是调用同一个方法?我想知道的是类ImpLog及其继承类是如何确定写入方式的(即文本还是数据库),请高手解答,谢了。

 回复 引用 查看   
#12楼[楼主] 2006-03-01 12:49 Terrylee      
@mrhgw
方法一和方法四都是.NET平台上的方法,而方法三和方法二都是Java平台上的,

具体写入日志的方式是在客户程序中确定的……

 回复 引用   
#13楼 2006-03-03 19:22 overred
工厂模式是桥模式的一个特定的实现
嘿嘿
还是希望Terrylee 多产

 回复 引用 查看   
#14楼 2006-03-15 12:58 突破自己      
请用桥接模式来改PetShop3.0的数据访问层
 回复 引用 查看   
#15楼[楼主] 2006-03-16 08:41 Terrylee      
@突破自己
PetShop3.0的数据访问层有必要用桥模式吗??

 回复 引用 查看   
#17楼[楼主] 2006-03-17 13:43 Terrylee      
@突破自己
也是一种实现思路,但是有问题。

有时间我会试着改一下的:)

 回复 引用   
#18楼 2006-06-11 11:37 过客111[未注册用户]
感觉这里的用法和适配器几乎一样,两者除了说法上不同,本质有什么区别?
 回复 引用 查看   
#19楼 2006-06-25 15:44 肖鹏      
@mrhgw
方式一和方式四调用的并不是同一个方法,方式二和方式三调用的也不是同一个方法。我也曾经在这一点上面有疑问。其实这里的关键是DatabaseLog和TextFileLog了解自己应该调用IImpleLog的哪个函数,也就是从函数名上说方式一和方式二调用的同一个函数名,方式三和方式四调用的是同一个函数名。从函数实现上说,这四个方式其实调用了四个不同的函数实现。你可以看看我的一篇文章:http://designpatterns.cnblogs.com/archive/2006/06/17/bridge_design_patterns.html。这是我的理解,很希望与大家交个朋友,多跟大家交流共同进步。

 回复 引用 查看   
#20楼[楼主] 2006-06-26 08:44 TerryLee      
@肖鹏
你的那篇文章写的不错,以后可以多多交流:-)

 回复 引用 查看   
#21楼 2006-06-26 10:36 肖鹏      
@TerryLee
谢谢,其实对于mrhgw的问题我也有疑问,就像我在我的文章最后写的“不过这里面还是有一点让人不舒服的地方,就是Editor中包含的两个方法,实际上对应了两种Editable。也许是这个例子举的不好。但是《Design patterns explained》中的例子和本例大同小异。”不知道你是怎么看待这个问题的?

 回复 引用   
#22楼 2006-06-30 12:52 3322[未注册用户]
我是初学者,看了一天早上你写的文章,感觉很不错,但是有一些暂时还不能理解,打算以后再看一遍,非常感谢!!!!
 回复 引用 查看   
#23楼[楼主] 2006-06-30 13:00 TerryLee      
@3322

不用客气,以后多交流:-)

 回复 引用   
#24楼 2006-08-17 14:59 txd_lf[未注册用户]
肖鹏说的没错,ImpLog至少应该有两个方法,一个是实现数据库的操作WriteDB,一个是实现文件的操作WriteTxt,ImpLog只是针对实现平台所提供的一些比较低级的操作。然后由Log的子类来决定调用那个。

而且ImgLog的子类不应该是.NET和Java平台的延伸(他们只是开发平台,而不是运行平台),而应该是Windows平台和Unix平台。

 回复 引用   
#25楼 2006-10-16 14:41 Frank Huang[未注册用户]
同意txd_lf的观点,这里我发表下个人的看法(如有不对之处希望大家指出)。现在我把ImpLog的代码应该改成:
public abstract class ImpLog

{

public abstract void WriteDB(string msg);
public abstract void WriteTxt(string msg);

}


public class NImpLog : ImpLog

{

public override void WriteDB(string msg)

{

//...... .NET平台下写DB

}

public override void WriteTxt(string msg)

{

//...... .NET平台下写Text

}

}


public class JImpLog : ImpLog

{

public override void WriteDB(string msg)

{

//...... JAVA平台下写DB

}

public override void WriteTxt(string msg)

{

//...... JAVA平台下写Text

}

}

现在我的问题是这样的,bridge pattern实际上是把M*N的继承关系转化成了M继承关系+N聚集关系,这样的转换在M和N易变的情况下是很有必要的。不过我同时发现,这样的转换却不得不增加ImpLog接口的复杂度,从我修改后的代码不难看出,M*N的继承关系实际上是被M*N个函数实现代替了(现在每各异个实现ImpLog的类都需要实现WriteDB和WriteTxt),请问我这样理解对不对?

 回复 引用   
#26楼 2006-10-16 14:50 Frank Huang[未注册用户]
补充下自己的观点。引用肖鹏文章开头引用的三句话中的第三句,Favor composition over inheritance. 这里大概就是这么个原则吧。
 回复 引用 查看   
#27楼[楼主] 2006-10-16 21:32 TerryLee      
@Frank Huang
可以这么说吧,一般情况下ImpLog接口比较稳定,所以这样的转换相对于复杂的继承关系来说还是值得的。

 回复 引用   
#28楼 2006-10-17 09:42 Frank Huang[未注册用户]
agree with you
 回复 引用 查看   
#29楼[楼主] 2006-10-17 20:23 TerryLee      
@Frank Huang
多多交流吧

 回复 引用   
#30楼 2006-10-18 10:04 Frank Huang[未注册用户]
@TerryLee
最近关注了你的系列文章,非常敬佩你的执着精神,希望再接再厉,给我们带来更为精彩的文章。向你学习!

 回复 引用 查看   
#31楼[楼主] 2006-10-18 21:39 TerryLee      
@Frank Huang
谢谢,呵呵:-)

 回复 引用   
#32楼 2006-10-19 11:26 joyli[未注册用户]
请教一下:在bridge中关于“确定是谁使用谁”,你是通过什么方法确定的呢?

 回复 引用 查看   
#33楼[楼主] 2006-10-19 12:56 TerryLee      
@joyli
能不能说具体一点呢?:)

 回复 引用   
#34楼 2006-10-19 17:28 joyli[未注册用户]
@各位

我认为bridge模式应该解决的是“类爆炸”的问题,例如我们可以把人分为
男人,女人(按性别分)
少年,青年,老年(按年龄分)
这样就会产生两种规则“gender_rule”“Age_rule”,如果按照继承的方式实现这些类的时候,就会产生以下类
1 class person
2 class man :pulic person
3 class woman :pulic person
4 class shaonian_man:public man
5 class zhongniannian_man:public man
6 class laonian_man:public man
7 class shaonian_woman :public woman
8 class zhongniannian_woman :public woman
9 class laonian_woman :public woman
如果现在在规则(gender_rule)上扩展other性别的时候,那么又会产生大量的类。

如果用bridge的方式我们按照不同规则抽象出不同的类:也就是应用bridge模式
例如:
class person
{
rule _rule;
}
class man :pulic person
class woman :pulic person

class rule
class laonian:pulic rule
class zhongnian:pulic rule
class qingnian :pulic rule

或者//


class person
{
rule _rule;
}
class laonian:pulic person
class zhongnian:pulic person
class qingnian :pulic person

class rule
class man :pulic rule
class woman :pulic rule


那么例如确定一个人是“中年男性”,那么在person和rule之间一定会存在一种aggregation的关系。

这个例子不太好,不知道意思说清楚了没有,请指教一下。多谢!这个例子好像更像是策略模式。

我想知道的是如何确定谁“聚集”谁的关系?在我看来他们谁包含谁都是可以的。

 回复 引用   
#35楼 2006-10-19 17:36 joyli[未注册用户]
@各位
关于log,写了一个完整的(表达意思而已,有些地方可能还不太好),不知道对不对,大家看看。
////.h
#include <string>
using namespace std;


class log
{
public:
virtual void write() = 0;
virtual std::string getclass() = 0;
protected:
logImpl* _plogImpl;
private:
};


class textlog : public log
{
public:
textlog(logImpl* value);
void write();
std::string getclass();
};
class dblog : public log
{
public:
dblog(logImpl* value);
void write();
std::string getclass();
};
//////
class logImpl
{
public:
virtual void execute(log* obj) = 0;
};

class jlog : public logImpl
{
private:
void writedb();
void writetext();
public:
jlog();
virtual void execute(log* obj);
};
class nlog : public logImpl
{
private:
void writedb();
void writetext();
public:
nlog();
virtual void execute(log* obj);//这里好像有点像visitor

};


////.cpp

#include "ceshi.h"

jlog::jlog()
{
}
void jlog::writetext()
{
printf("this is java txt log");
}
void jlog::writedb()
{
printf("this is java db log");
}
void jlog::execute(log* obj)
{
if (obj->getclass() == "textlog") {writetext();}
if (obj->getclass() == "dblog") {writedb();}
}

nlog::nlog()
{

}
void nlog::writetext()
{
printf("this is dotnet db log");
}
void nlog::writedb()
{
printf("this is dotnet txt log");
}
void nlog::execute(log* obj)
{
if (obj->getclass() == "textlog") {writetext();}
if (obj->getclass() == "dblog") {writedb();}
}



textlog::textlog(logImpl* value)
{
_plogImpl = value;
}
void textlog::write()
{
_plogImpl->execute(this);
}
std::string textlog::getclass()
{
return "textlog";
}


dblog::dblog(logImpl* value)
{
_plogImpl = value;
}
void dblog::write()
{
_plogImpl->execute(this);
}
std::string dblog::getclass()
{
return "dblog";
}

//main

int main(int argc, char* argv[])
{
log* pobjlog = new textlog(new jlog()); //java环境下的text日志
pobjlog->write();
delete pobjlog;
return 0;
}

 回复 引用 查看   
#36楼[楼主] 2006-10-19 19:52 TerryLee      
@joyli
“类爆炸”还只是现象,“类爆炸”的产生是由于某些类在多个维度或者说多个方向上的变化而造成的。

 回复 引用   
#37楼 2006-11-01 11:24 Tkfudan[未注册用户]
@joyli
这个好像不是bridge模式吧。似乎他们之间的聚合关系太紧密了。
还有class log
{
public:
virtual void write() = 0;
virtual std::string getclass() = 0;
protected:
logImpl* _plogImpl;
private:
};

write直接改写下面是不是更好?
virtual void write()
{
_plogImpl->execute(this);
}

 回复 引用   
#38楼 2006-11-19 13:09 付海涛[未注册用户]
我感觉桥接模式用来解决对象的组合是比较方便的,比如厂商和产品的组合、毛笔和颜料的组合、车辆和配件等的组合是极为方便的。
但是本例子中的日志,我感觉也是把多继承变成了多函数的使用。即便是结合访问者模式使调用方统一接口,也要使用多态,产生了很多函数。

 回复 引用 查看   
#39楼 2007-01-31 22:46 shenfx      
@overred
更准确地说:抽象工厂模式才是桥模式的一个特定的实现

 回复 引用   
#40楼 2007-02-08 11:27 [匿名] [未注册用户]
好文
 回复 引用 查看   
#41楼 2007-03-10 18:02 YaziMyWife      
你写的日志的例子感觉你用工厂方法实现还好些啊
 回复 引用   
#42楼 2007-03-24 11:39 waiting[未注册用户]
我 第一次在 你这里看到 “多维度的变化”, 例子十分恰当 很好, 我看到的 Bridge Pattern中最好的一个了 。
 回复 引用   
#43楼 2007-04-10 15:09 沈政[未注册用户]
多谢李老师在设计模式方面的透彻讲解!
 回复 引用 查看   
#44楼 2008-01-25 10:04 捕捉梦想      
请问一下.它与装饰模式有什么区别呀!我怎么看差不多呀?
 回复 引用   
#45楼 2008-03-15 11:40 henry015[未注册用户]
public class DatabaseLog : Log

{
public override void Write(string log)
{
implementor.Execute(log);
}
}

方法Execute似乎只管平台的事情(不需要知道日记要记录成什么格式),
而Write()方法只管与本实体相关的日记格式问题,
那么在Write方法或Execute方法中又是如何做到格式和平台的关联的?
(即做到关联而又不会新增类)能不能给个思路解释下


 回复 引用 查看   
#46楼 2008-04-09 22:07 水言木      
感谢博主美文,每次都是看完李建忠老师的设计模式WebCast,再来看楼主的文章,然后自己再写一个总结,受益匪浅:)
 回复 引用 查看   
#47楼 2008-11-11 09:54 Flicker      
受教了,谢谢!
 回复 引用 查看   
#48楼[楼主] 2008-11-12 11:14 TerryLee      
@Flicker
不用客气:)

 回复 引用 查看   
#49楼 2009-01-06 15:35 Fengdesudu      
真是不啬赐教,学习了
 回复 引用 查看   
#50楼[楼主] 2009-01-06 18:35 TerryLee      
@Fengdesudu
:)

 回复 引用 查看   
#51楼 2009-01-07 14:22 pillow      
我用工厂方法又封装了一次。
 回复 引用 查看   
#52楼 2009-02-02 17:47 Elian      
感觉写日志的例子很奇怪

看着看着我也产生了和11楼同样的疑问...

感觉吕老师的这个例子比较容易懂一些
http://www.cnblogs.com/zhenyulu/articles/67016.html

 回复 引用 查看   
#53楼 2009-07-07 15:40 OG      
真不错,简单明了
 回复 引用 查看   
#54楼 2009-08-05 09:45 月云      
呵……
感觉这个bridge模式实现的要点应该就是我在您的上一篇文章中说的原来那个一直不懂问题了——以对象组合替代对象多继承,从而实现类的单一原则。对吗?呵呵……

 回复 引用 查看   
#55楼 2009-08-27 16:09 轻风随风      
@Frank Huang
赞成你的说法。

 回复 引用 查看   
#56楼 2009-08-27 16:16 Astar      
测试评论
 回复 引用 查看   
#57楼 2009-08-29 15:52 轻风随风      
@Frank Huang
补充:我又想了想,这里的需求一旦变化,现在需要把日志写入xml文件中,此时我们不得不用一个XMLLog类继承自Log类,然后需要把
public abstract class ImpLog
{
public abstract void WriteDB(string msg);
public abstract void WriteTxt(string msg);
}
修改为:
public abstract class ImpLog
{
public abstract void WriteDB(string msg);
public abstract void WriteTxt(string msg);
public abstract void WriteXML(string msg);
}
而继承自ImpLog的所有子类也要同时修改,绝对违反了开放封闭原则。
经过思考后,我想这种桥接模式的实现并不好。不过可以用partial关键字修改类进行改善。
我是新手,不知道想法对不对,希望大家批评指正。

 回复 引用 查看   
#58楼 2009-10-14 12:46 云淡风轻-.net      
比较好懂
 回复 引用   
#59楼 2009-10-28 12:28 Yeson[未注册用户]
如果不仅仅是两个业务维度在剧烈变化呢?比如说3个,Log Level也掺和进来,那么就XML下 也要根据LOG LEVEL来进行扩展 又出现不同的子类, XMLLEVEL1 XMLLEVEL2.。。
 回复 引用   
#60楼 2009-11-15 20:29 锦瑟[未注册用户]
public class DatabaseLog : Log

{

public override void Write(string log)

{

implementor.Execute(log);

}

}

public class TextFileLog : Log

{

public override void Write(string log)

{

implementor.Execute(log);

}

}

感觉有问题啊,2个类都调用同样的方法,那么这个Execute方法里到底是写的数据库的方式还是textfile方式呢?

如果是分成2个方法:
public abstract class ImpLog
{
public abstract void WriteDB(string msg);
public abstract void WriteTxt(string msg);
}
那么DB和Txt的log方式都已经在implog的子类里实现了,还要dbfilelog和textfilelog两个类干什么?


 回复 引用 查看   
#61楼 2009-11-18 19:49 只是找工的      
写得不错,学习的好地方。。
 回复 引用 查看   
#62楼 2009-11-24 14:04 Coder Lee      
本来以为这个模式很复杂了
不过一看那个图,第一个图,豁然开朗。
不就是Has-A的应用吗。
写的让人感觉很容易理解!

 回复 引用 查看   
#63楼 2010-01-29 23:48 于江朋      
学习的过程中,发现自己的渺小
 回复 引用 查看   
#64楼 2010-08-16 17:08 天才小啊呆      
李大哥,总觉得,这个跟策略模式一样。。。您能谈一下他们之间的差异么
 回复 引用 查看   
#65楼 2010-10-13 10:53 YoungSin      
Favor composition over inheritance.
桥接模式节省不了代码量,但是从可读性上感觉是要比纯粹的继承 要好的多了!

 回复 引用 查看   
#66楼 2010-12-08 14:56 许志伟      
大哥 上面的implementor的UML图是不是错了 应该不是菱形 是直线箭头
 回复 引用 查看   
#67楼 2011-01-18 15:19 王波洋      
强烈建议附加源码
 回复 引用 查看   
#68楼 2011-03-14 11:37 shfong      
@TerryLee
您好,看了文章,觉得很不错,同时也看完了评论,有点困惑,你能解释下11楼,45楼,57楼,60楼的迷惑吗?我觉得也许有很多人有这样的问题,希望楼主解释下,谢谢,期待楼主更好的文章!

 回复 引用 查看   
#69楼 2011-06-21 15:31 KarlWang      
菜鸟学习进行中
 回复 引用 查看   
#70楼 2011-08-23 17:10 nd      
感觉和工厂方法模式很像的!