结构型设计模式.docx_第1页
结构型设计模式.docx_第2页
结构型设计模式.docx_第3页
结构型设计模式.docx_第4页
结构型设计模式.docx_第5页
已阅读5页,还剩94页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1. 适配器模式(Adapter Pattern).NET设计模式系列之八Terrylee,2006年2月概述在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。那么如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?这就是本文要说的Adapter 模式。意图将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。结构图图1 类的Adapter模式结构图图2 对象的Adapter模式结构图生活中的例子适配器模式允许将一个类的接口转换成客户期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。扳手提供了一个适配器的例子。一个孔套在棘齿上,棘齿的每个边的尺寸是相同的。在美国典型的边长为1/2和1/4。显然,如果不使用一个适配器的话,1/2的棘齿不能适合1/4的孔。一个1/2至1/4的适配器具有一个1/2的阴槽来套上一个1/2的齿,同时有一个1/4的阳槽来卡入1/4的扳手。 图3使用扳手适配器例子的适配器对象图适配器模式解说我们还是以日志记录程序为例子说明Adapter模式。现在有这样一个场景:假设我们在软件开发中要使用一个第三方的日志记录工具,该日志记录工具支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,它提供给我们的API接口是Write()方法,使用方法如下:Log.Write(Logging Message!);当软件系统开发进行到一半时,处于某种原因不能继续使用该日志记录工具了,需要采用另外一个日志记录工具,它同样也支持数据库日志记录DatabaseLog和文本文件记录FileLog两种方式,只不过它提供给我们的API接口是WriteLog()方法,使用方法如下:Log.WriteLog(Logging Message!);该日志记录工具的类结构图如下:图4日志记录工具类结构图它的实现代码如下:public abstract class LogAdapteepublic abstract void WriteLog();public class DatabaseLog:LogAdapteepublic override void WriteLog()Console.WriteLine(Called WriteLog Method);public class FileLog:LogAdapteepublic override void WriteLog()Console.WriteLine(Called WriteLog Method);在我们开发完成的应用程序中日志记录接口中(不妨称之为ILogTarget接口,在本例中为了更加清楚地说明,在命名上采用了Adapter模式中的相关角色名字),却用到了大量的Write()方法,程序已经全部通过了测试,我们不能去修改该接口。代码如下:public interface ILogTargetvoid Write();这时也许我们会想到修改现在的日志记录工具的API接口,但是由于版权等原因我们不能够修改它的源代码,此时Adapter模式便可以派上用场了。下面我们通过Adapter模式来使得该日志记录工具能够符合我们当前的需求。前面说过,Adapter模式有两种实现形式的实现结构,首先来看一下类适配器如何实现。现在唯一可行的办法就是在程序中引入新的类型,让它去继承LogAdaptee类,同时又实现已有的ILogTarget接口。由于LogAdaptee有两种类型的方式,自然我们要引入两个分别为DatabaseLogAdapter和FileLogAdapter的类。图5 引入类适配器后的结构图实现代码如下:public class DatabaseLogAdapter:DatabaseLog,ILogTargetpublic void Write()WriteLog();public class FileLogAdapter:FileLog,ILogTargetpublic void Write()this.WriteLog();这里需要注意的一点是我们为每一种日志记录方式都编写了它的适配类,那为什么不能为抽象类LogAdaptee来编写一个适配类呢?因为DatabaseLog和FileLog虽然同时继承于抽象类LogAdaptee,但是它们具体的WriteLog()方法的实现是不同的。只有继承于该具体类,才能保留其原有的行为。我们看一下这时客户端的程序的调用方法:public class Apppublic static void Main()ILogTarget dbLog = new DatabaseLogAdapter();dbLog.Write(Logging Database.);ILogTarget fileLog = new FileLogAdapter();fileLog.Write(Logging File.);下面看一下如何通过对象适配器的方式来达到我们适配的目的。对象适配器是采用对象组合而不是使用继承,类结构图如下:图6引入对象适配器后的结构图实现代码如下:public class LogAdapter:ILogTargetprivate LogAdaptee _adaptee;public LogAdapter(LogAdaptee adaptee)this._adaptee = adaptee;public void Write()_adaptee.WriteLog();与类适配器相比较,可以看到最大的区别是适配器类的数量减少了,不再需要为每一种具体的日志记录方式来创建一个适配器类。同时可以看到,引入对象适配器后,适配器类不再依赖于具体的DatabaseLog类和FileLog类,更好的实现了松耦合。再看一下客户端程序的调用方法:public class Apppublic static void Main()ILogTarget dbLog = new LogAdapter(new DatabaseLog();dbLog.Write(Logging Database.);ILogTarget fileLog = new LogAdapter(new FileLog();fileLog.Write(Logging Database.);通过Adapter模式,我们很好的实现了对现有组件的复用。对比以上两种适配方式,可以总结出,在类适配方式中,我们得到的适配器类DatabaseLogAdapter和FileLogAdapter具有它所继承的父类的所有的行为,同时也具有接口ILogTarget的所有行为,这样其实是违背了面向对象设计原则中的类的单一职责原则,而对象适配器则更符合面向对象的精神,所以在实际应用中不太推荐类适配这种方式。再换个角度来看类适配方式,假设我们要适配出来的类在记录日志时同时写入文件和数据库,那么用对象适配器我们会这样去写:public class LogAdapter:ILogTargetprivate LogAdaptee _adaptee1;private LogAdaptee _adaptee2;public LogAdapter(LogAdaptee adaptee1,LogAdaptee adaptee2)this._adaptee1 = adaptee1;this._adaptee2 = adaptee2;public void Write()_adaptee1.WriteLog();_adaptee2.WriteLog();如果改用类适配器,难道这样去写:public class DatabaseLogAdapter:DatabaseLog,FileLog,ILogTargetpublic void Write()/WriteLog();显然是不对的,这样的解释虽说有些牵强,也足以说明一些问题,当然了并不是说类适配器在任何情况下都不使用,针对开发场景不同,某些时候还是可以用类适配器的方式。.NET中的适配器模式1Adapter模式在.NET Framework中的一个最大的应用就是COM Interop。COM Interop就好像是COM和.NET之间的一条纽带,一座桥梁。我们知道,COM组件对象与.NET类对象是完全不同的,但为了使COM客户程序象调用COM组件一样调用.NET对象,使.NET程序象使用.NET对象一样使用COM组件,微软在处理方式上采用了Adapter模式,对COM对象进行包装,这个包装类就是RCW(Runtime Callable Wrapper)。RCW实际上是runtime生成的一个.NET类,它包装了COM组件的方法,并内部实现对COM组件的调用。如下图所示:图7 .NET程序与COM互相调用示意图2.NET中的另一个Adapter模式的应用就是DataAdapter。ADO.NET为统一的数据访问提供了多个接口和基类,其中最重要的接口之一是IdataAdapter。与之相对应的DataAdpter是一个抽象类,它是ADO.NET与具体数据库操作之间的数据适配器的基类。DataAdpter起到了数据库到DataSet桥接器的作用,使应用程序的数据操作统一到DataSet上,而与具体的数据库类型无关。甚至可以针对特殊的数据源编制自己的DataAdpter,从而使我们的应用程序与这些特殊的数据源相兼容。注意这是一个适配器的变体。实现要点1Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。2Adapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。3Adapter模式的实现可以非常的灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。4Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便的适配。以上几点引用自MSDN WebCast效果对于类适配器:1用一个具体的Adapter类对Adaptee和Taget进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。2使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。3仅仅引入了一个对象,并不需要额外的指针一间接得到Adaptee.对于对象适配器:1允许一个Adapter与多个Adaptee,即Adaptee本身以及它的所有子类(如果有子类的话)同时工作。Adapter也可以一次给所有的Adaptee添加功能。2使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。适用性在以下各种情况下使用适配器模式:1系统需要使用现有的类,而此类的接口不符合系统的需要。2想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。3(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。总结总之,通过运用Adapter模式,就可以充分享受进行类库迁移、类库重用所带来的乐趣。 参考资料阎宏,Java与模式,电子工业出版社James W. Cooper,C#设计模式,电子工业出版社Alan Shalloway James R. Trott,Design Patterns Explained,中国电力出版社MSDN WebCast C#面向对象设计模式纵横谈(7):Adapter 适配器模式(结构型模式)作者:TerryLee出处: 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 2. 桥接模式(Bridge Pattern).NET设计模式系列之九Terrylee,2006年2月概述在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用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同样我们没有修改任何的东西,只是增加了两个继承于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平台 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(); 另一种实现代码:using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Bridge_Pattern public interface ImpLog void Execute(string Log); public interface ILog void Write(ImpLog imlog,string Log); public class DataBaseLog : ILog public void Write(ImpLog imlog, string Log) imlog.Execute(Log); public class FileLog : ILog public void Write(ImpLog imlog, string Log) imlog.Execute(Log); public class NImpLog : ImpLog public void Execute(string Log) Console.WriteLine(NET平台:+Log); /. .NET平台 public class JImpLog : ImpLog public void Execute(string Log) /. Java平台 Console.WriteLine(Java平台:+Log); class Program static void Main(string args) ILog dlog = new DataBaseLog(); ImpLog imlog = new NImpLog(); dlog.Write(imlog, DataBase); ILog flog = new FileLog(); ImpLog fmlog = new JImpLog(); flog.Write(fmlog, FileLog); Console.Read(); 可能有人会担心说,这样不就又增加了客户程序与具体日志记录方式之间的耦合性了吗?其实这样的担心是没有必要的,因为这种耦合性是由于对象的创建所带来的,完全可以用创建型模式去解决,就不是这里我们所讨论的内容了。最后我们再来考虑一个问题,为什么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出处: 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 3 装饰模式(Decorator Pattern).NET设计模式系列之十Terrylee,2006年3月概述在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?这就是本文要讲的Decorator模式。意图动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。GOF 设计模式结构图图1 Decorator模式结构图生活中的例子装饰模式动态地给一个对象添加额外的职责。不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。图2 使用有画框的画作为例子的装饰模式对象图装饰模式解说在软件开发中,经常会遇到动态地为一个对象而不是整个类增加一些功能的问题,还是以我惯用的记录日志的例子来说明吧(也许在Decorator模式里面用这个例子不是特别合适)。现在要求我们开发的记录日志的组件,除了要支持数据库记录DatabaseLog和文本文件记录TextFileLog两种方式外,我们还需要在不同的应用环境中增加一些额外的功能,比如需要记录日志信息的错误严重级别,需要记录日志信息的优先级别,还有日志信息的扩展属性等功能。在这里,如果我们不去考虑设计模式,解决问题的方法其实很简单,可以通过继承机制去实现,日志类结构图如下:图3实现代码如下:public abstract class Log public abstract void Write(string log);public class DatabaseLog : Log public override void Write(string log) /.记录到数据库中 public class TextFileLog : Log public override void Write(string log) /.记录到文本文件中 需要记录日志信息的错误严重级别功能和记录日志信息优先级别的功能,只要在原来子类DatabaseLog和TextFileLog的基础上再生成子类即可,同时需要引进两个新的接口IError和I Priority,类结构图如下:图4 实现代码如下:public interface IError void SetError();public interface IPriority void SetPriority();public class DBErrorLog : DatabaseLog, IError public override void Write(string log) base.Write(log); public void SetError() /.功能扩展,实现了记录错误严重级别 public class DBPriorityLog : DatabaseLog, IPriority public override void Write(string log) base.Write(log); public void SetPriority() /.功能扩展,实现了记录优先级别 public class TFErrorLog : TextFileLog, IError public override void Write(string log) base.Write(log); public void SetError() /.功能扩展,实现了记录错误严重级别 public class TFPriorityLog : TextFileLog, IPriority public override void Write(string log) base.Write(log); public void SetPriority() /.功能扩展,实现了记录优先级别 此时可以看到,如果需要相应的功能,直接使用这些子类就可以了。这里我们采用了类的继承方式来解决了对象功能的扩展问题,这种方式是可以达到我们预期的目的。然而,它却带来了一系列的问题。首先,前面的分析只是进行了一种功能的扩展,如果既需要记录错误严重级别,又需要记录优先级时,子类就需要进行接口的多重继承,这在某些情况下会违反类的单一职责原则,注意下图中的蓝色区域:图5 实现代码:public class DBEPLog : DatabaseLog, IError, IPriority public override void Write(string log) SetError();SetPriority(); base.Write(log); public void SetError() /.功能扩展,实现了记录错误严重级别 public void SetPriority() /.功能扩展,实现了记录优先级别 public class TFEPLog : DatabaseLog, IError, IPriority public override void Write(string log) SetError();SetPriority();base.Write(log); public void SetError() /.功能扩展,实现了记录错误严重级别 public void SetPriority() /.功能扩展,实现了记录优先级别 其次,随着以后扩展功能的增多,子类会迅速的膨胀,可以看到,子类的出现其实是DatabaseLog和TextFileLog两个子类与新增加的接口的一种排列组合关系,所以类结构会变得很复杂而难以维护,正如象李建忠老师说的那样“子类复子类,子类何其多”;最后,这种方式的扩展是一种静态的扩展方式,并没有能够真正实现扩展功能的动态添加,客户程序不能选择添加扩展功能的方式和时机。现在又该是Decorator模式出场的时候了,解决方案是把Log对象嵌入到另一个对象中,由这个对象来扩展功能。首先我们要定义一个抽象的包装类LogWrapper,让它继承于Log类,结构图如下:图6实现代码如下:public abstract class LogWrapper : Log private Log _log; public LogWrapper(Log log) _log = log; public override void Write(string log) _log.Write(log); 现在对于每个扩展的功能,都增加一个包装类的子类,让它们来实现具体的扩展功能,如下图中绿色的区域:图7实现代码如下:public class LogErrorWrapper : LogWrapper public LogErrorWrapper(Log _log) :base(_log) public override void Write(string log) SetError(); /.功能扩展 base.Write(log); public void SetError() /.实现了记录错误严重级别 public class LogPriorityWrapper : LogWrapper public LogPriorityWrapper(Log _log) : base(_log) public override void Write(string log) SetPriority(); /.功能扩展 base.Write(log); public void SetPriority() /.实现了记录优先级别 到这里,LogErrorWrapper类和LogPriorityWrapper类真正实现了对错误严重级别和优先级别的功能的扩展。我们来看一下客户程序如何去调用它:public class Program public static void Main(string args) Log log = new DatabaseLog(); LogWrapper lew1 = new LogErrorWrapper(log); /扩展了记录错误严重级别 lew1.Write(Log Message); LogPriorityWrapper lpw1 = new LogPriorityWrapper(log); /扩展了记录优先级别 lpw1.Write(Log Message); LogWrapper lew2 = new LogErrorWrapper(log); LogPriorityWrapper lpw2 = new LogPriorityWrapper(lew2); /这里是lew2 /同时扩展了错误严重级别和优先级别 lpw2.Write(Log Message); 注意在上面程序中的第三段装饰才真正体现出了Decorator模式的精妙所在,这里总共包装了两次:第一次对log对象进行错误严重级别的装饰,变成了lew2对象,第二次再对lew2对象进行装饰,于是变成了lpw2对象,此时的lpw2对象同时扩展了错误严重级别和优先级别的功能。也就是说我们需要哪些功能,就可以这样继续包装下去。到这里也许有人会说LogPriorityWrapper类的构造函数接收的是一个Log对象,为什么这里可以传入LogErrorWrapper对象呢?通过类结构图就能发现,LogErrorWrapper类其实也是Log类的一个子类。我们分析一下这样会带来什么好处?首先对于扩展功能已经实现了真正的动态增加,只在需要某种功能的时候才进行包装;其次,如果再出现一种新的扩展功能,只需要增加一个对应的包装子类(注意:这一点任何时候都是避免不了的),而无需再进行很多子类的继承,不会出现子类的膨胀,同时Decorator模式也很好的符合了面向对象设计原则中的“优先使用对象组合而非继承”和“开放-封闭”原则。.NET中的装饰模式1.NET中Decorator模式一个典型的运用就是关于Stream,它存在着如下的类结构:图8可以看到, Buffered

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论