封装变化—工厂模式简介—日志记录.docx_第1页
封装变化—工厂模式简介—日志记录.docx_第2页
封装变化—工厂模式简介—日志记录.docx_第3页
封装变化—工厂模式简介—日志记录.docx_第4页
封装变化—工厂模式简介—日志记录.docx_第5页
已阅读5页,还剩1页未读 继续免费阅读

下载本文档

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

文档简介

封装变化工厂模式简介日志记录考虑一个日志记录工具。目前需要提供一个方便的日志API,使得客户可以轻松地完成日志的记录。该日志要求被记录到指定的文本文件中,记录的内容属于字符串类型,其值由客户提供。我们可以非常容易地定义一个日志对象:public class Logpublic void Write(string target, string log)/实现内容;当客户需要调用日志的功能时,可以创建日志对象,完成日志的记录:Log log = new Log();log.Write(“error.log”, “log”);然而随着日志记录的频繁使用,有关日志的文件越来越多,日志的查询与管理也变得越不方便。此时,客户提出,需要改变日志的记录方式,将日志内容写入到指定的数据表中。显然,如果仍然按照前面的设计,具有较大的局限性。现在我们回到设计之初,想象一下对于日志API的 设计,需要考虑到这样的变化吗?这里存在两种设计理念,即渐进的设计和计划的设计。从本例来分析,要求设计者在设计初就考虑到日志记录方式在未来的可能变 化,并不容易。再者,如果在最开始就考虑全面的设计,会产生设计上的冗余。因此,采用计划的设计固然具有一定的前瞻性,但一方面对设计者的要求过高,同时 也会产生一些缺陷。那么,采用渐进的设计时,遇到需求变化时,利用重构的方法,改进现有的设计,又需要考虑未来的再一次变化吗?这是一个见仁见智的问题。 对于本例而言,我们完全可以直接修改Write()方法,接受一个类型判断的参数,从而解决此问题。但这样的设计,自然要担负因为未来可能的再一次变化,而导致代码大量修改的危险,例如,我们要求日志记录到指定的Xml文件中。所以,变化是完全可能的。在时间和技术能力允许的情况下,我更倾向于将变化对设计带来的影响降低到最低。此时,我们需要封装变化。在 封装变化之前,我们需要弄清楚究竟是什么发生了变化?从需求看,是日志记录的方式发生了变化。从这个概念分析,可能会导致两种不同的结果。一种情形是,我 们将日志记录的方式视为一种行为,确切的说,是用户的一种请求。另一种情形则从对象的角度来分析,我们将各种方式的日志看作不同的对象,它们调用接口相同 的行为,区别仅在于创建的是不同的对象。前者需要我们封装“用户请求的变化”,而后者需要我们封装“日志对象创建的变化”。封装“用户请求的变化”,在这里就是封装日志记录可能的变化。也就是说,我们需要把日志记录行为抽象为一个单独的接口,然后才分别定义不同的实现。如图一所示:图一:封装日志记录行为的变化如果熟悉设计模式,可以看到图一所表示的结构正是Command模式的体现。由于我们对日志记录行为进行了接口抽象,用户就可以自由地扩展日志记录的方式,只需要实现ILog接口即可。至于Log对象,则存在与ILog接口的弱依赖关系:public class Logprivate ILog log;public Log(ILog log)this.log = log;public void Write(string target, string logValue)log.Execute(target, logValue);我们也可以通过封装“日志对象创建的变化”实现日志API的可扩展性。在这种情况下,日志会根据记录方式的不同,被定义为不同的对象。当我们需要记录日志时,就创建相应的日志对象,然后调用该对象的Write()方法,实现日志的记录。此时,可能会发生变化的是需要创建的日志对象,那么要封装这种变化,就可以定义一个抽象的工厂类,专门负责日志对象的创建,如图二所示:图二:封装日志对象创建的变化图二是Factory Method模式的体现,由抽象类LogFactory专门负责Log对象的创建。如果用户需要记录相应的日志,例如要求日志记录到数据库,需要先创建具体的LogFactory对象:LogFactory factory = new DBLogFactory();当在应用程序中,需要记录日志,那么再通过LogFactory对象来获取新的Log对象:Log log = factory.Create();log.Write(“ErrorLog”, “log”);如果用户需要改变日志记录的方式为文本文件时,仅需要修改LogFactory对象的创建即可:LogFactory factory = new TxtFileLogFactory();为了更好地理解“封装对象创建的变化”,我们再来看一个例子。假如,我们需要设计一个数据库组件,它能够访问微软的Sql Server数据库。根据ADO.Net的知识,我们需要使用如下的对象:SqlConnection, SqlCommand, SqlDataAdapter等。如果仅就Sql Server而言,在访问数据库时,我们可以直接创建这些对象:SqlConnection connection = new SqlConnection(strConnection);SqlCommand command = new SqlCommand(connection);SqlDataAdapter adapter = new SqlDataAdapter();如果在一个数据库组件中,充斥着如上的语句,显然是不合理的。它充满了僵化的坏味道,一旦要求支持其他数据库时,原有的设计就需要彻底的修改,这为扩展带来了困难。那么我们来思考一下,以上的设计应该做怎样的修改?假定该数据库组件要求或者将来要求支持多种数据库,那么对于Connection,Command,DataAdapter等对象而言,就不能具体化为Sql Server的对象。也就是说,我们需要为这些对象建立一个继承的层次结构,为他们分别建立抽象的父类,或者接口。然后针对不同的数据库,定义不同的具体类,这些具体类又都继承或实现各自的父类,例如Connection对象:图三:Connection对象的层次结构我为Connection对象抽象了一个统一的IConnection接口,而支持各种数据库的Connection对象都实现了IConnection接口。同样的,Command对象和DataAdapter对象也采用了相似的结构。现在,我们要创建对象的时候,可以利用多态的原理创建:IConnection connection = new SqlConnection(strConnection);从 这个结构可以看到,根据访问的数据库的不同,对象的创建可能会发生变化。也就是说,我们需要设计的数据库组件,以现在的结构来看,仍然存在无法应对对象创 建发生变化的问题。利用“封装变化”的原理,我们有必要把创建对象的责任单独抽象出来,以进行有效地封装。例如,如上的创建对象的代码,就应该由专门的对 象来负责。我们仍然可以建立一个专门的抽象工厂类DBFactory,并由它负责创建Connection,Command,DataAdapter对象。至于实现该抽象类的具体类,则与目标对象的结构相同,根据数据库类型的不同,定义不同的工厂类,类图如图四所示:图四:DBFactory的类图图四是一个典型的Abstract Factory模式的体现。类DBFactory中的各个方法均为abstract方法,所以我们也可以用接口来代替该类的定义。继承DBFactory类的各个具体类,则创建相对应的数据库类型的对象。以SqlDBFactory类为例,创建各自对象的代码如下:public class SqlDBFactory: DBFactorypublic override IConnection CreateConnection(string strConnection)return new SqlConnection(strConnection);public override ICommand CreateCommand(IConnection connection)return new SqlCommand(connection);public override IDataAdapter CreateDataAdapter()return new SqlDataAdapter();现在要创建访问Sql Server数据库的相关对象,就可以利用工厂类来获得。首先,我们可以在程序的初始化部分创建工厂对象:DBFactory factory = new SqlDBFactory();然后利用该工厂对象创建相应的Connection,Command等对象:IConnection connection = factory.CreateConnection(strConnection);ICommand command = factory.CreateCommand(connection);由于我们利用了封装变化的原理,建立了专门的工厂类,以封装对象创建的变化。可以看到,当我们引入工厂类后,Connection,Command等对象的创建语句中,已经成功地消除了其与具体的数据库类型相依赖的关系。在如上的代码中,并未出现Sql之类的具体类型,如SqlConnection、SqlCommand等。也就是说,现在创建对象的方式是完全抽象的,是与具体实现无关的。无论是访问何种数据库,都与这几行代码无关。至于涉及到的数据库类型的变化,则全部抽象到DBFactory抽象类中了。需要更改访问数据库的类型,我们也只需要修改创建工厂对象的那一行代码,例如将Sql Server类型修改为Oracle类型:DBFactory factory = new OracleDBFactory();很 显然,这样的方式提高了数据库组件的可扩展性。我们将可能发生变化的部分封装起来,放到程序固定的部分,例如初始化部分,或者作为全局变量,更可以将这些 可能发生变化的地方,放到配置文件中,通过读取配置文件的值,创建相对应的对象。如此一来,不需要修改代码,也不需要重新编译,仅仅是修改xml文件,就能实现数据库类型的改变。例如,我们创建如下的配置文件:创建工厂对象的代码则相应修改如下:string factoryName = ConfigurationSettings.AppSetting

温馨提示

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

评论

0/150

提交评论