帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > ASP.NET编程 >
利用.NET的Reflection增强对象工厂的扩展性
作者:sam1111 发布时间:2005-03-12 来源:ASPCOOL
    
  
  对象工厂
  
  对象工厂(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 编写手机应用程序  (2005-03-12)
 ·利用J2ME与ASP建立数据库连接  (2005-03-12)
 ·正确利用J2EE的各种工具  (2005-03-12)
 ·利用实体EJB来避免性能缺陷:一种  (2005-03-12)
 ·利用JSP建立Web站点  (2005-03-12)
 ·利用Editplus2的用户工具功能来即  (2005-03-11)
 ·利用static实现表格的颜色隔行显  (2005-03-11)

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·放眼未来 帝国近期将有重大举措!
·PHPWind6.3.2版通行证发布
·帝国备份王2008版正式发布
·帝国备份王2008版发布
·phpcms2007转帝国CMS5.0程序发布
·dedecms5.1转帝国CMS5.0程序发布
·帝国网站管理系统V5.0商业购买说明
   类别最新
·ASP.NET中为DataGrid添加合计字段
·.text urlRewrite介绍
·利用 ASP.NET 的内置功能抵御 Web
·ASP.NET Cache
·用 WebClient.UploadData 方法 上载
·ASP.NET 程序设计-序
·什么是客户端/伺服端(Client/Serve
·因特网应用程序的开发
·网页的种类
·.NET Framework-Microsoft Visual
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统