本文由sam1111授权aspcool.com发表,未经作者许可,严禁转载。
对象工厂
对象工厂(object factory)模式通常被用来从一个派生系统中产生某个对象,并将其作为基类的实例返回,从而获得基类的接口,并尽量掩盖派生类的细节,以便充分利用面向对象的多态性来获得强大的功能。通常,对象工厂的实现方法是,在一个工厂方法中,先利用一个基于类型标记(type tag)的switch语句找出适当的类型,然后创建该类型的实例并返回之。
举例来说,设想一个图形系统,它包括了线、圆、矩形等元素,这些元素具有一些公共的操作,比如draw、resize等。那么我们可能具有下面这样一个继承体系:
为了能够在对象工厂中区分这些类,我们还需要为它们分别指定一个类型标记。这些类型标记可以是enumeration、整数、字符串等能够唯一地标记这些类的值。用它们的类名字符串作为标记看起来不错。我们可以使用下面这个对象工厂来创建shape对象:
public sealed class shapefactory
{
private shapefactory()
{
}
public static baseshape createshape(string shapeid)
{
switch(shapeid)
{
case "rectangle":
return new rectangle();
case "circle":
return new circle();
case "line":
return new line();
default:
return null;
}
}
}
shapefacory的唯一用途就是用来创建shape实例,我们不希望它本身那能够被继承,或者能够被实例化,因此,它被声明为sealed,并具有一个私有构造函数。当我们需要得到某个shape的实例时,只要调用shapefactory的createshape()方法,并传入一个适当的shapeid字符串,createshape()就会为我们返回正确的shape实例了。
增强扩展性
现在我们拥有了一个shape工厂,它工作的不错。但是这个工厂具有一个明显的不足:难以扩充。每当系统中新增加一个shape类时,我们都不得不修改createshape()方法,向其中加入新的case语句。这在我们的产品没发布之前还好,我们可以完全控制我们的代码。但当我们的产品发布之后,用户可以很容易地从baseshape派生自己的shape类,但他们却很难利用createshape()方法将他们的shape类加入到系统中,因为他们无法修改createshape()方法的实现。因此这个shape工厂还需要一些扩展性。但是解决这个问题的一个主要的障碍是,如果不使用switch语句,那么createshape()将无法预先知道到底存在哪些shape类,以及类型标记与具体的类之间的关系。
alexandrescu在他的《modern c++ design》中针对这个问题给出了一个c++解法。他在对象工厂类中利用一个std::map来维护类型标记与类型的创建方法之间的关系,并在对象工厂类中增加了两个接口register()和unregister(),用来在此map中注册或注销类型标记和类型创建方法。每增加一个shape类时,需要同时为这个类写一个匿名名字空间,在此空间中调用register()方法,将自己的类型标记和创建方法注册到对象工厂中。
在c#中,我们可以借鉴alexandrescu的方法,在对象工厂中利用一个hashtable来维护类型标记与类型之间的关系,利用register()方法来注册。每个shape类必须负责自己的注册工作,因此我们为每个shape类增加一个registershape()方法,它调用shapefactory.register()来注册自己。但是有两个问题:
1、 对registershape()方法的调用应当何时进行呢?
2、 c#并不支持匿名名字空间,那么如何来调用每个shape类的registershape()方法呢?
对于第一个问题,我们有一个时机,那就是在createshape()方法被调用之前,各个shape类必须已经完成了注册。在静态构造函数中完成对registershape()的调用到是个不错的选择。
对于第二个问题,我们可以使用reflection机制,首先遍历所有的类型,找出由baseshape派生的类型,然后分别调用它们的registershape()方法。
按照以上思路,shapefactory的实现代码如下:
public sealed class shapefactory
{
private static hashtable _creationmap = null;
static shapefactory()
{
_creationmap = new hashtable();
assembly a = typeof(shapefactory).module.assembly;
module[] modules = a.getmodules();
for(int i = 0; i < modules.length; i++)
{
type[] types = modules[i].gettypes();
for(int j = 0; j < types.length; j++)
{
if(!types[j].equals(typeof(baseshape))
&& types[j].basetype != null
&& types[j].basetype.equals(typeof(baseshape)))
{
object obj = types[j].invokemember(null,
bindingflags.declaredonly
bindingflags.public
bindingflags.instance
bindingflags.createinstance,
null, null, null);
types[j].invokemember("registershape",
bindingflags.declaredonly
bindingflags.public bindingflags.nonpublic
bindingflags.instance bindingflags.invokemethod,
null, obj, null);
}
}
}
}
public static void register(string shapeid, type shape)
{
if(!_creationmap.containskey(shapeid))
_creationmap.add(shapeid, shape);
}
public static baseshape createshape(string shapeid)
{
type shape = (type)_creationmap[shapeid];
if(shape == null)
return null;
return (baseshape)shape.invokemember(null,
bindingflags.declaredonly
bindingflags.public
bindingflags.instance bindingflags.createinstance,
null, null, null);
}
}
在createshape()方法第一次被调用之前,静态构造函数会先执行。它利用reflection机制遍历所有的类型,对于由baseshape派生的类型,先创建一个实例,然后调用其regisershape()方法,将其在hashtable中注册。当createshape()方法被调用时,根据传入的shapeid从hashtable中取出相应的类型,并返回其实例。这样,我们就有了一个具有一定扩展性的shape工厂。
总结
在对象工厂中增加reflection机制,可以在一定程度上增强对象工厂的扩展性。改进后的shapefactory不用再在每次增加了shape类之后进行修改了,只要新加入的shape类实现了regisershape()方法,它就能够被注册到对象工厂中,并被正确地创建。这样,我们甚至可以方便地为我们的系统实现插件功能。比如,我们可以指定一个插件目录,遍历这个目录,将其中的shape类注册到工厂中。用户只需将他们的插件拷贝到这个目录下即可。
当然,reflection也许并不是此问题的最佳解决方案。它需要遍历系统中所有的类型,执行效率不够高。还好静态构造函数只会被执行一次。希望本文能够起到抛砖引玉的作用。如果您有更好的方案,欢迎和我交流。我的联系方式:sam1111@citiz.net
http://blog.joycode.com/sam1111/
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器