利用.NET的Reflection增强对象工厂的扩展性

原创 2003年06月20日 13:40:00

对象工厂<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

对象工厂(Object Factory)模式通常被用来从一个派生系统中产生某个对象,并将其作为基类的实例返回,从而获得基类的接口,并尽量掩盖派生类的细节,以便充分利用面向对象的多态性来获得强大的功能。通常,对象工厂的实现方法是,在一个工厂方法中,先利用一个基于类型标记(type tag)的switch语句找出适当的类型,然后创建该类型的实例并返回之。

 

举例来说,设想一个图形系统,它包括了线、圆、矩形等元素,这些元素具有一些公共的操作,比如DrawResize等。那么我们可能具有下面这样一个继承体系:

 

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

 

为了能够在对象工厂中区分这些类,我们还需要为它们分别指定一个类型标记。这些类型标记可以是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的实例时,只要调用ShapeFactoryCreateShape()方法,并传入一个适当的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

 

让你的代码有更好的扩展性

      代码有良好的扩展性,这每个开发人员的愿望。可是在无尽的需求改动增加和改动下,还有多少人在时时刻刻坚持。更多的时候是思考不到位或贪一时之快写下很多的if/else或者“硬代码”,为后续的需求...
  • greatwzz
  • greatwzz
  • 2010年07月07日 18:27
  • 1094

可扩展性数据库的架构设计

扩展性与硬件 随着系统的膨胀,硬件的可扩展性体现在增加资源,提高性能的能力上,如添加更多的处理器、内存等。 扩展性与软件 扩展性要求软件能够有效地利用硬件的能力,软件的设计应该支持并...
  • fwj380891124
  • fwj380891124
  • 2012年07月13日 08:47
  • 4745

.Net Reflection机制

1、什么是反射       Reflection,中文翻译为反射。        这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Modu...
  • Elegant_Design
  • Elegant_Design
  • 2007年01月04日 21:36
  • 2549

asp.net的10个提升性能或扩展性的秘密(二)

简介 Asp.net有很多值得你挖掘的“秘密”,当你发现了它们,将会给你网站的性能和可扩展性带来巨大提升!例如,对于Membership以及Profile提供程序有一些秘密的瓶颈,它们很容易...
  • yanghua_kobe
  • yanghua_kobe
  • 2011年10月13日 21:29
  • 2969

C++利用反射调用C#函数

1.引入.net命名空间   using namespace  System; using namespace System::Windows::Forms; using namespace ...
  • biyusr
  • biyusr
  • 2012年05月22日 08:44
  • 1152

Object Factories(对象工厂)

浅谈C++泛型编程与设计模式---对象工厂
  • zjq2008wd
  • zjq2008wd
  • 2015年02月13日 15:03
  • 1285

Java反射(二)利用反射分析类的能力

利用反射分析类能力的类的源码如下(来源于《Java核心技术》卷一): package testreflection; import java.util.*; import java.lang.ref...
  • l294265421
  • l294265421
  • 2015年06月26日 21:35
  • 626

javascript 对象的可扩展性详解

javascript 对象中的可扩展性指的是:是否可以给对象添加新属性。所有的内置对象和自定义对象显示的都是可扩展的,对于宿主对象,则有javascript 引擎决定。       下面有几个函数是设...
  • xingjigongsi
  • xingjigongsi
  • 2015年01月14日 00:32
  • 1452

java的反射机制Reflection(重点、难点)

1、在集合中必须使用泛型;
  • lpp1234567
  • lpp1234567
  • 2014年03月10日 16:59
  • 739

C#的Reflection总结

什么是反射 在.NET中的反射也可以实现从对象的外部来了解对象(或程序集)内部结构的功能,哪怕你不知道这个对象(或程序集)是个什么东西,另外.NET中的反射还可以运态创建出对象并执行它其中的方法。 ...
  • cxihu
  • cxihu
  • 2016年03月03日 22:16
  • 502
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:利用.NET的Reflection增强对象工厂的扩展性
举报原因:
原因补充:

(最多只允许输入30个字)