概述
在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?提供一种封装机制来隔离出“这个易变对象”的变化,从而保持系统中“其它依赖该对象的对象”不随着需求的改变而改变?这就是要说的Factory Method模式了。
意图
定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
例子
1.IHttpHandlerFactory.GetHandler是一个工厂方法模式的典型例子
2.IEnumerable和IEnumerator就是一个Creator和一个Product
3.System.Security.Cryptography中关于加密算法的选择
UML类图
代码:对应UML类图
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 抽象工厂
9 /// </summary>
10 public abstract class Creator
11 {
12 public abstract Product FactoryMethod();
13 }
14}
15
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 抽象产品
9 /// </summary>
10 public abstract class Product
11 {
12
13 }
14}
15
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 具体工厂A
9 /// </summary>
10 class ConcreteCreatorA : Creator
11 {
12 public override Product FactoryMethod()
13 {
14 return new ConcreteProductA();
15 }
16 }
17}
18
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 产品A
9 /// </summary>
10 class ConcreteProductA : Product
11 {
12 }
13}
14
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 工厂B
9 /// </summary>
10 class ConcreteCreatorB : Creator
11 {
12 public override Product FactoryMethod()
13 {
14 return new ConcreteProductB();
15 }
16 }
17}
18
2using System.Collections.Generic;
3using System.Text;
4
5namespace DesignPatterns.FactoryMethod.UMLCode
6{
7 /// <summary>
8 /// 产品B
9 /// </summary>
10 class ConcreteProductB : Product
11 {
12 }
13}
14
代码:完整例子
下面以<ASP.NET中请求处理通道中的实例>来说明工厂方法的运用(引用于Terrylee大哥文章)
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,通过它来创建HttpApplication实例。GetApplicationInstance使用HttpContext实例来确定针对这个请求该响应哪个虚拟路径,如果这个虚拟路径以前请求过,HttpApplication(或者一个继承于ASP.Global_asax的类的实例)将直接从应用程序池中返回,否则针对该虚拟路径将创建一个新的HttpApplication对象并返回。如下图所示:
HttpApplicationFactory.GetApplicationInstance带有一个类型为HttpContext的参数,创建的所有对象(产品)都是HttpApplication的类型,通过反编译,来看一下GetApplicationInstance的实现:
2{
3 if (HttpApplicationFactory._customApplication != null)
4 {
5 return HttpApplicationFactory._customApplication;
6 }
7 if (HttpDebugHandler.IsDebuggingRequest(context))
8 {
9 return new HttpDebugHandler();
10 }
11 if (!HttpApplicationFactory._theApplicationFactory._inited)
12 {
13 lock (HttpApplicationFactory._theApplicationFactory)
14 {
15 if (!HttpApplicationFactory._theApplicationFactory._inited)
16 {
17 HttpApplicationFactory._theApplicationFactory.Init(context);
18 HttpApplicationFactory._theApplicationFactory._inited = true;
19 }
20 }
21 }
22 return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
23}
24
System.Web.IHttpHandlerFactory
我们来做进一步的探索,HttpApplication实例需要一个Handler对象来处理资源请求, HttpApplication的主要任务就是找到真正处理请求的类。HttpApplication首先确定了一个创建Handler对象的工厂,来看一下在Machine.config文件中的配置区<httphandlers>,在配置文件注册了应用程序的具体处理类。例如在Machine.config中对*.aspx的处理将映射到System.Web.UI.PageHandlerFactory 类,而对*.ashx的处理将映射到System.Web.UI.SimpleHandlerFactory类,这两个类都是继承于IhttpHandlerFactory接口的具体类:
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
</httpHandlers>
这个配置区建立了资源请求的类型和处理请求的类之间的一个映射集。如果一个.aspx页面发出了请求,将会调用System.Web.UI.PageHandlerFactory类,HttpApplication调用接口IHttpHandlerFactory中的工厂方法GetHandler来创建一个Handler对象。当一个名为sample.aspx的页面发出请求时,通过PageHandlerFactory将返回一个ASP.SamplePage_aspx对象(具体产品),如下图:
IHttpHandlerFactory工厂:
2{
3 // Methods
4 IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
5 void ReleaseHandler(IHttpHandler handler);
6}
7
IHttpHandlerFactory.GetHandler是一个工厂方法模式的典型例子,在这个应用中,各个角色的设置如下:
抽象工厂角色:IHttpHandlerFactory
具体工厂角色:PageHandlerFactory
抽象产品角色:IHttpHandler
具体产品角色:ASP.SamplePage_aspx
进一步去理解
理解上面所说的之后,我们就可以去自定义工厂类来对特定的资源类型进行处理。第一步我们需要创建两个类去分别实现IHttpHandlerFactory和IHttpHandler这两个接口。
2
3 IHttpHandler IHttpHandlerFactory.GetHandler(
4 HttpContext context, String requestType,
5 String url, String pathTranslated ) {
6
7 return new HttpHandlerImpl();
8
9 }//IHttpHandlerFactory.GetHandler
10
11 void IHttpHandlerFactory.ReleaseHandler(
12 IHttpHandler handler) { /*no-op*/ }
13
14}//HttpHandlerFactoryImpl
15
16public class HttpHandlerImpl:IHttpHandler {
17
18 void IHttpHandler.ProcessRequest(HttpContext context) {
19
20 context.Response.Write("sample handler invoked");
21
22 }//ProcessRequest
23
24 bool IHttpHandler.IsReusable { get { return false; } }
25
26}//HttpHandlerImpl
27
第二步需要在配置文件中建立资源请求类型和处理程序之间的映射。我们希望当请求的类型为*.sample时进入我们自定义的处理程序,如下:
<add verb="*" path="*.sample"
type="HttpHandlerFactoryImpl,SampleHandler" />
</httpHandlers>
最后一步我们需要把文件扩展*.sample映射到ASP.NET ISAPI扩展DLL(aspnet_isapi.dll)上。由于我们已经建立了用于处理新扩展文件的处理程序了,我们还需要把这个扩展名告诉IIS并把它映射到ASP.NET。如果你不执行这个步骤而试图访问*.sample文件,IIS将简单地返回该文件而不是把它传递给ASP.NET运行时。其结果是该HTTP处理程序不会被调用。
运行Internet服务管理器,右键点击默认Web站点,选择属性,移动到主目录选项页,并点击配置按钮。应用程序配置对话框弹出来了。点击添加按钮并在可执行字段输入aspnet_isapi.dll文件路径,在扩展字段输入.sample。其它字段不用处理;该对话框如下所示:
在.NET Framework中,关于工厂模式的使用有很多的例子,例如IEnumerable和IEnumerator就是一个Creator和一个Product;System.Security.Cryptography中关于加密算法的选择,SymmetricAlgorithm, AsymmetricAlgorithm, 和HashAlgorithm分别是三个工厂,他们各有一个静态的工厂方法Create;System.Net.WebRequest是 .NET Framework 的用于访问 Internet 数据的请求/响应模型的抽象基类。使用该请求/响应模型的应用程序可以用协议不可知的方式从 Internet 请求数据。在这种方式下,应用程序处理 WebRequest 类的实例,而协议特定的子类则执行请求的具体细节。请求从应用程序发送到某个特定的 URI,如服务器上的 Web 页。URI 从一个为应用程序注册的 WebRequest子代列表中确定要创建的适当子类。注册 WebRequest子代通常是为了处理某个特定的协议(如 HTTP 或 FTP),但是也可以注册它以处理对特定服务器或服务器上的路径的请求。
总结
抽象工厂解决的是单个易变对象的创建问题。
源代码:[下载]
参考
1. 【dofactory】ttp://www.dofactory.com/Patterns/Patterns.aspx#list
2. 【Terrylee】http://www.cnblogs.com/Terrylee/archive/2006/07/17/334911.html
3. 【卢振宇老师】http://zhenyulu.cnblogs.com/category/6930.html