zoukankan      html  css  js  c++  java
  • factory

    增强扩展性

     

    现在我们拥有了一个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的实现代码如下:



    //file1.cs
    //csc /t:library file1.cs
    using System;
    using System.Reflection;
    using System.Collections;
    namespace MyNamespace1
    {
        
    #region 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, 
                                
    nullnullnull);
                            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, 
                    
    nullnullnull);
            }

        }

        
    #endregion


        
    #region Shape Class
        
    public abstract  class BaseShape
        
    {
            
            
    abstract public void print();
            
    abstract public void RegisterShape();
        }


        
    public class  Rectangle:BaseShape
        
    {
            
    public override void print()
            
    {
                Console.Write(
    "Rectangle\n");
            }

            
    public override void RegisterShape()
            
    {
                ShapeFactory.Register(
    "Rectangle",typeof(Rectangle));
            }

        }

        
    public class  Circle:BaseShape
        
    {
            
    public override void print()
            
    {
                Console.Write(
    "Circle\n");
            }

            
    public override void RegisterShape()
            
    {
                ShapeFactory.Register(
    "Circle",typeof(Circle));
            }

        }

        
    #endregion

        
    public class MyClass1
        
    {
            
    public static void Main1()
            
    {
                ShapeFactory.CreateShape(
    "Circle").print();
                Console.ReadLine();
                
    //System.Reflection.BindingFlags.GetProperty
            }

        }

    }



    CreateShape()方法第一次被调用之前,静态构造函数会先执行。它利用Reflection机制遍历所有的类型,对于由BaseShape派生的类型,先创建一个实例,然后调用其RegiserShape()方法,将其在Hashtable中注册。当CreateShape()方法被调用时,根据传入的shapeID从Hashtable中取出相应的类型,并返回其实例。这样,我们就有了一个具有一定扩展性的Shape工厂。

     

    总结

     

    在对象工厂中增加Reflection机制,可以在一定程度上增强对象工厂的扩展性。改进后的ShapeFactory不用再在每次增加了Shape类之后进行修改了,只要新加入的Shape类实现了RegiserShape()方法,它就能够被注册到对象工厂中,并被正确地创建。这样,我们甚至可以方便地为我们的系统实现插件功能。比如,我们可以指定一个插件目录,遍历这个目录,将其中的Shape类注册到工厂中。用户只需将他们的插件拷贝到这个目录下即可。

     

    当然,Reflection也许并不是此问题的最佳解决方案。它需要遍历系统中所有的类型,执行效率不够高。还好静态构造函数只会被执行一次。希望本文能够起到抛砖引玉的作用。如果您有更好的方案,欢迎和我交流。我的联系方式:sam1111@citiz.net

     

  • 相关阅读:
    Vue 框架怎么实现对象和数组的监听?
    能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
    vue-router 路由模式有几种?
    Vue 组件间通信有哪几种方式?
    v-model 的原理?
    华硕笔记本修复
    linux下制作u盘启动盘
    virtualbox不能启动虚拟机
    ubuntu14.04建立wifi热点
    git中文文件名和中文目录显示乱码
  • 原文地址:https://www.cnblogs.com/snowball/p/161321.html
Copyright © 2011-2022 走看看