快捷搜索:

.NET设计模式:工厂方法模式(Factory Method)

概述

在软件系统中,常常面临着“某个工具”的创建事情,因为需求的变更,这个工具的详细实现常常面临着剧烈的变更,然则它却拥有对照稳定的接口。若何应对这种变更?供给一种封装机制来隔离出“这个易变工具”的变更,从而维持系统中“其它依附该工具的工具”不跟着需求的改变而改变?这便是要说的Factory Method模式了。

意图

定义一个用户创建工具的接口,让子类抉择实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

布局图

生活中的例子

工厂措施定义一个用于创建工具的接口,然则让子类抉择实例化哪个类。压注成型演示了这种模式。塑料玩具制造商加工塑料粉,将塑料注入到盼望外形的模具中。玩具的种别(车,人物等等)是由模具抉择的。

工厂措施讲解

在工厂措施模式中,核心的工厂类不再认真所有产品的创建,而是将详细创建事情交给子类去做。这个核心类仅仅认真给出详细工厂必须实现的接口,而不打仗哪一个产品类被实例化这种细节。这使得工厂措施模式可以容许系统在不改动工厂角色的环境下引进新产品。在Factory Method模式中,工厂类与产品类每每具有平行的等级布局,它们之间逐一对应。

现在我们斟酌一个日志记录的例子(这里我们只是为了阐明Factory Method模式,实际项目中的日志记录不会这么去做,也要比这繁杂一些)。假定我们要设计日志记录的类,支持记录的措施有FileLog和EventLog两种要领。在这里我们先不谈设计模式,那么这个日志记录的类就很好实现了:

1/**////

2/// 日志记录类

3///

4public class Log

5{

6

7public void WriteEvent()

8{

9Console.WriteLine("EventLog Success!");

10}

11

12public void WriteFile()

13{

14Console.WriteLine("FileLog Success!");

15}

16

17public void Write(string LogType)

18{

19switch(LogType.ToLower())

20{

21case "event":

22WriteEvent();

23break;

24

25case "file":

26WriteFile();

27break;

28

29default:

30break;

31}

32}

33}

34

这样的法度榜样布局显然不能相符我们的要求,假如我们增添一种新的日志记录的要领DatabaseLog,那就要改动Log类,跟着记录要领的变更,switch语句在赓续的变更,这样就引起了全部利用法度榜样的不稳定,进一步阐发上面的代码,发明对付EventLog和FileLog是两种完全不合的记录要领,它们之间不应该存在一定的联系,而应该把它们分手作为零丁的工具来对待。

1/**////

2/// EventLog类

3///

4public class EventLog

5{

6public void Write()

7{

8Console.WriteLine("EventLog Write Success!");

9}

10}

11

12/**////

13/// FileLog类

14///

15public class FileLog

16{

17public void Write()

18{

19Console.WriteLine("FileLog Write Success!");

20}

21}

22

进一步抽象,为它们抽象出一个合营的父类,布局图如下:

实今世码:

1/**////

2/// Log类

3///

4public abstract class Log

5{

6public abstract void Write();

7}

8

此时EventLog和FileLog类的代码应该如下:

1/**////

2/// EventLog类

3///

4public class EventLog:Log

5{

6public override void Write()

7{

8Console.WriteLine("EventLog Write Success!");

9}

10}

11/**////

12/// FileLog类

13///

14public class FileLog:Log

15{

16public override void Write()

17{

18Console.WriteLine("FileLog Write Success!");

19}

20}

21

此时我们再看增添新的记录日志要领DatabaseLog的时刻,必要做哪些工作?只必要增添一个承袭父类Log的子类来实现,而无需再去改动EventLog和FileLog类,这样的设计满意了类之间的层次关系,又很好的相符了面向工具设计中的单一职责原则,每一个类都只认真一件详细的工作。到这里彷佛我们的设计很完美了,事实上我们还没有看客户法度榜样若何去调用。 在利用法度榜样中,我们要应用某一种日志记录要领,大概会用到如下这样的语句:

EventLog eventlog = new EventLog();

eventlog.Write();

当日志记录的要领从EventLog变更为FileLog,我们就得改动所有法度榜样代码中呈现上面语句的部分,这样的事情量是可想而知的。此时就必要解耦详细的日志记录要领和利用法度榜样。这就要引入Factory Method模式了,每一个日志记录的工具便是工厂所天生的产品,既然有两种记录要领,那就必要两个不合的工厂去临盆了,代码如下:

1/**////

2/// EventFactory类

3///

4public class EventFactory

5{

6public EventLog Create()

7{

8return new EventLog();

9}

10}

11/**////

12/// FileFactory类

13///

14public class FileFactory

15{

16public FileLog Create()

17{

18return new FileLog();

19}

20}

21

这两个工厂和详细的产品之间是平行的布局,并逐一对应,并在它们的根基上抽象出一个公用的接口,布局图如下:

实今世码如下:

1/**////

2/// LogFactory类

3///

4public abstract class LogFactory

5{

6public abstract Log Create();

7}

8

此时两个详细工厂的代码应该如下:

1/**////

2/// EventFactory类

3///

4public class EventFactory:LogFactory

5{

6public override EventLog Create()

7{

8return new EventLog();

9}

10}

11/**////

12/// FileFactory类

13///

14public class FileFactory:LogFactory

15{

16public override FileLog Create()

17{

18return new FileLog();

19}

20}

21

这样经由过程工厂措施模式我们把上面那工具创建事情封装在了工厂中,此时我们彷佛完成了全部Factory Method的历程。这样达到了我们利用法度榜样和详细日志记录工具之间解耦的目的了吗?看一下此时客户端法度榜样代码:

1/**////

2/// App类

3///

4public class App

5{

6public static void Main(string[] args)

7{

8LogFactory factory = new EventFactory();

9

10Log log = factory.Create();

11

12log.Write();

13}

14}

15

在客户法度榜样中,我们有效地避免了详细产品工具和利用法度榜样之间的耦合,可是我们也看到,增添了详细工厂工具和利用法度榜样之间的耦合。那这样究竟带来什么好处呢?我们知道,在利用法度榜样中,Log工具的创建是频繁的,在这里我们可以把

LogFactory factory = new EventFactory();

这句话放在一个类模块中,任何必要用到Log工具的地方仍旧不变。如果换一种日志记录要领,只要改动一处为:

LogFactory factory = new FileFactory();

另外的任何地方我们都不必要去改动。有人会说那照样改动代码,其其实开拓中我们很难避免改动,然则我们可以只管即便做到只改动一处。

着实使用.NET的特点,我们可以避免这种不需要的改动。下面我们使用.NET中的反射机制来进一步改动我们的法度榜样,这时就要用到设置设置设备摆设摆设文件了,假如我们想应用哪一种日志记录要领,则在响应的设置设置设备摆设摆设文件中设置如下:

1

2

3

4

此时客户端代码如下:

1/**////

2/// App类

3///

4public class App

5{

6public static void Main(string[] args)

7{

8string strfactoryName = ConfigurationSettings.AppSettings["factoryName"];

9

10LogFactory factory;

11factory = (LogFactory)Assembly.Load("FactoryMethod").CreateInstance("FactoryMethod." + strfactoryName);

12

13Log log = factory.Create();

14log.Write();

15}

16}

17

现在我们看到,在引进新产品(日志记录要领)的环境下,我们并不必要去改动工厂类,而只是增添新的产品类和新的工厂类(留意:这是任何时刻都不能避免的),这样很好的相符了开放封闭原则。

ASP.NET HTTP通道中的利用

Factory Method模式在ASP.NET HTTP通道中我们可以找到很多的例子。ASP.NET HTTP通道是System.Web命名空间下的一个类,WEB Server应用该类处置惩罚接管到的HTTP哀求,并给客户端发送相应。HTTP通道主要的事情有Session治理,利用法度榜样池治理,缓存治理,安然等。

System.Web.HttpApplicationFactory

HttpRuntime是HTTP通道的进口点,它根据每一个详细的哀求创建一个HttpContext实例, HttpRuntime并没有确定它将要处置惩罚哀求的HttpApplication工具的类型,它调用了一个静态的工厂措施HttpApplicationFactory.GetApplicationInstance,经由过程它来创建HttpContext实例。GetApplicationInstance应用HttpContext实例来确定针对这个哀求该相应哪个虚拟路径,假如这个虚拟路径曩昔哀求过,HttpApplication(或者一个承袭于ASP.Global_asax的类的实例)将直接从利用法度榜样池中返回,否则针对该虚拟路径将创建一个新的HttpApplication工具并返回。如下图所示:

HttpApplicationFactory.GetApplicationInstance带有一个类型为HttpContext的参数,创建的所有工具(产品)都是HttpApplication的类型,经由过程反编译,来看一下GetApplicationInstance的实现:

1internal static IHttpHandler GetApplicationInstance(HttpContext context)

2{

3if (HttpApplicationFactory._customApplication != null)

4{

5return HttpApplicationFactory._customApplication;

6}

7if (HttpDebugHandler.IsDebuggingRequest(context))

8{

9return new HttpDebugHandler();

10}

您可能还会对下面的文章感兴趣: