石头的.NET天空

明天的幸福靠今天修——专注于.NET技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

应用框架的抽象工厂设计

Posted on 2007-11-17 02:05  .NET Rock  阅读(1780)  评论(3)    收藏  举报
    抽象工厂这个设计模式想必大家都很熟悉了,简单的说使用这个技术可以将使用业务对象的客户代码和创建业务对象的代码分离,以期达到程序的松耦合,减少对具体对象的直接依赖,今天在这里也无意深入探讨这个模式的原理,GOF的书中是这样概括它的:“提供一个创建一系列相关或互相依赖的对象的接口,而无须指定它们具体的类”,呵呵,读起来有那么点晦涩,下面简单的看一副图来说明这个模式以及它的应用:

这里设置了一个生产灯座的场景,Lampholder代表灯座类,它派生于AbstractLampholder(抽象灯座)类,在这里客户组件如果想要创建Lampholder的实例可以通过工厂来获得,也就是下面这行代码:

AbstractLampholder lph = LampholderFactory.GetLampholder();

这样便是使用了抽象工厂,使得具体类的创建与客户代码分离了,这样具体类的变更不再会影响到我们的客户程序。恩,看起来不错,工厂发挥作用了!但我们再仔细想一下,没错客户组件不在依赖于具体类,可却依赖于工厂类了,假设现在需求发生变更,用户又研发了一种新的灯座,规格样式和功能都与老灯座不同,要求把现有系统中的老灯座全部换为新研发的灯座,于是我们的业务组件里又多出了一个新类,叫做NewLampholder,同样派生与AbstractLampholder类,面对这样的变更,我们最好的方式是加一个新的工厂NewLampholderFactory从这里面创建NewLampholder的对象并返回给客户组件,呵呵,老的问题又来了我们要在客户程序里把

AbstractLampholder lph = LampholderFactory.GetLampholder();

这行代码统统改为:

AbstractLampholder lph = NewLampholderFactory.GetLampholder();

面对这种变更似乎抽象工厂也失去它应有的作用了,它只是把依赖从具体类换成了工厂类。当然,你可以说只要简单的修改LampholderFactory类的GetLampholder方法就可以了啊,干吗要搞这么麻烦还针对新灯座专门创建一个工厂类!没错,仅仅在这个场景里是没问题的,但现实的场景要比这复杂的多,可能会有很多种不同的产品会有很多的抽象工厂,如果直接改即有的工厂代码是不现实的,并且面向对象的设计方法更重扩展而不推崇修改,就像微软发布产品的新版本也是往里面加新东西而不去改老的东西。

        那么现在瓶颈提出来了,针对这个问题我做了一个新的设计,也就是今天所要讲述的应用框架的设计,我的想法是对于各种各样的应用我们应该设计一套基础的通用的框架,这个框架里应该包括大多数应用都会使用到的一些基础性的功能,例如:缓存、配置、身份验证、授权、加密、事件日志等等。呵呵,不过这些都不是今天的主题,今天的主题是把类工厂服务加到将要设计的这套应用框架中,下图描述了我新的设计:

相信大家看到这个图都明白我的意思了,但我还是简要说两句吧,我在抽象工厂和业务对象上面又加了一层,叫做应用框架层(AppFramework),在这个框架里我定义了一个类是ClassFactoryServices,然后里面定义了一个返回通用类型对象的方法GetFactory,这个方法专门用来产生工厂类的实例,方法里接受客户组件传过来的一个参数,这个参数的内容定义在客户组件的配置文件中,也就是这一行:

<Class name="LampholderFactory" type="NewLampholderFactory" />

这个name代表我想要创建的工厂的一个标识,这个标识只做为一个参数传递,不起实际作用,起作用的是name对应的type,我在GetFactory方法里将使用.NET反射技术来创建type包含的类的实例,也就是我们的新灯座工厂(NewLampholderFactory)类的实例,大家看一看这样是不是解决了刚刚在上文中提到的客户组件依赖于类工厂的问题,如果需求变更又要换回老灯座,只需要简单的修改一下配置文件就可以了,简单的将type的值从NewLampholderFactory改为LampholderFactory就OK了。也许有人会说了,像这样一行代码:

ClassFactoryServices.GetFactory("LampholderFactory")

现在看起来是没问题,但假设我的需求是留一部分老灯座换一部分新灯座,怎么办?就算你加多一个配置节:

<Classname="LampholderFactory-old"type="LampholderFactory" />

那还不是要多处的修改客户程序把给GetFactory传递的参数替换过来,现在又依赖起字符串来了。呵呵,依赖是一定有的,松耦合也不是不耦合,那我想到的只是你可以按修改量来改,如果要求系统中要保留大部分的老灯座可以直接把:

<Classname="LampholderFactory"type="NewLampholderFactory" />换成

<Classname="LampholderFactory"type="LampholderFactory" />然后再加一个配置节:

<Classname="LampholderFactory-new"type="NewLampholderFactory" />

反之则相反处理。呵呵,我也没想到更好的方法,如果您有更好的方法欢迎您指教,大家一起进步嘛!呵呵,晚了,睡了!