代码改变世界

Architecture & Pattern & Practise 之 我也IoC

2010-03-05 00:11  姜 萌@cnblogs  阅读(514)  评论(0编辑  收藏  举报

对于IoC,第一次接触这个概念在当初学Spring的时候,当时觉得这种设计真是合理,使用IoC我们就能将我们创建的对象管理起来,而不必在对象间手动传来传去还要自己维护。使用Spring更能增强我们对denpendency relation的理解。

Ioc英文为 Inversion of Control,即反转模式,后被大牛Martin Fowler改名为 Dependency Injection 依赖注入,就是将组件之间的关系通过第三方进行注射,不需要类自己在代码中去使用Get/Set解决调用关系。

今天我们模拟写一个IoC。通过配置文件来进行组件注册。

先看看流行的.NET下几种优秀IoC

Castle:(官方站点http://www.castleproject.org/

说明 不使用配置文件,容器使用的是一个第三方的组件:Windsor。由容器自动管理组件之间的依赖关系,但使用框架这不容易控制组件间的依赖关系

Spring.Net(Spring官方站点http://www.springframework.net/

说明 由java平台上的Spring移植过来,在java平台上有大量拥护者和学习资源,都可以为.net所用。并且现在的spring可以使用注解(java里叫Annotation,类似于类似.net中的Attribute)以减少配置文件的负担。

Unity(CodePlex页面http://www.codeplex.com/unity

说明 来自微软的Practise & Pattern团队,是Enterprise Library中的一个应用程序块。轻量级,可扩展。

相关资料:http://www.kaiyuan8.org/Article/UyyIMwNiXBYThOIhEvaU.aspxhttp://www.codeplex.com/unity

如下就是我们这个demo级的IoC整体设计

image

image

IApplicationContext想用户提供使用IoC的接口,它本身继承IContext。

IContext用于存储每个上下文信息和注册的组件。

IRegister(组件注册器)用于向组件注入值,每个注册器中都有一个CurrentContext,当上下文获得注册器时会自动配置此值。同时含有一个IObjectInjector组件用注入。

IResourceManager用于读取配置信息,含有一个IReadAdapter,顾名思义,这是一个适用于各种存储方式的读取适配器接口,比如xml文件,或者是调用远程服务等,不管数据是怎么获得到的,最终对外都提供为XML数据。

配置文件:根节点为Goh(正式的应该加上namespace和xsd文件),每个组件的元素叫Component,Component有name和type属性,还有<Property>子元素用来表示对象中的属性(Property),每个Property元素有name、value、ref三种属性。ref可以用来引用Component。

主要接口定义

public interface IContext
    {
        object this[string name] { get; set; }
        bool QueryExists(string name);
        bool RemoveComponent(string name);
        Guid ContextId { get; }
        IRegister Register{ get; }
    }

public interface IRegister
    {
        /// <summary>
        /// 将上下文中的名为targetName的对象的property属性设置为property值。默认的反射搜索设置为:BindingFlags.NonPublic | BindingFlags.CreateInstance
        /// </summary>
        /// <param name="targetName">目标的属性名称</param>
        /// <param name="property">注入的属性值</param>
        void InjectProperty(string targetName, string propertyName, object property);
        void InjectProperty(string targetName, string propertyName, object property, BindingFlags bindingFlags);
        IContext CurrentContext { get; set; }
    }

public interface IConfigureManager
    {
        IContext GenerateContext();
        /// <summary>
        /// 自动根据先前配置存储配置IContext对象
        /// </summary>
        /// <param name="context">GlobalHandler的应用程序上下文</param>
        void ConfigContext(IContext context);
    }

public interface IReadAdapter
    {
        XElement Config { get; }
    }

 

实现代码

public class ConfigureManager : IConfigureManager
    {
        #region Fields
        private IReadAdapter _reader;
        #endregion

        #region Constructors & Initializer
        public ConfigureManager(IReadAdapter reader)
        {
            _reader = reader;
        }
        #endregion

        #region IConfigureManager 成员
        public IContext GenerateContext()
        {
            return GenerateComponentTable()["applicationContext"] as IContext; ;
        }

        public void ConfigContext(IContext context)
        {
            Dictionary<string, object> dict = GenerateComponentTable();
            foreach(var key in dict.Keys)
            {
                context[key] = dict[key];
            }
        }

        #endregion

        #region private Helper Methods
        private Dictionary<string, object> GenerateComponentTable()
        {
            Dictionary<string, object> dict = new Dictionary<string, object>();
            XElement config =_reader.Config;
            var components = config.Elements("Component");
            //先不注入属性而是仅将未初始化的对象存入dict。
            foreach(XElement elmt in components)
            {
                string name = elmt.Attribute("name").Value;
                string valueType = elmt.Attribute("type").Value;
                Type type = Type.GetType(valueType);
                object realValue = TypeInitializer.Instance.CreateInitObject(type);
                dict.Add(name, realValue);
            }

            foreach(XElement elmt2 in components)
            {
                string curName = elmt2.Attribute("name").Value;
                var properties = elmt2.Elements("Property");
                foreach(XElement prop in properties)
                {
                    string propName = prop.Attribute("name").Value;
                    XAttribute valueAttr = prop.Attribute("value");
                    if(valueAttr != null)
                    {
                        string propValue = valueAttr.Value;
                        ObjectInjector injector = new ObjectInjector();
                        Type propType = injector.GetPropertyType(dict[curName], propName);
                        object realValue = null;
                        if(propType == typeof(int))
                        {
                            realValue = TypeInitializer.Instance.TransformStringToInteger(propValue);
                        }
                        else
                        {
                            realValue = propValue;
                        }
                        injector.InjectProperty(dict[curName], propName, realValue);
                    }
                    XAttribute refAttr = prop.Attribute("ref");
                    if(refAttr != null)
                    {
                        object refValue = dict[refAttr.Value];
                        ObjectInjector injector = new ObjectInjector();
                        injector.InjectProperty(dict[curName], propName, refValue);
                    }
                }
            }
            return dict;
        }
        #endregion
    }


public class Context : IContext
    {
        #region Fields
        private Guid _contextId;
        private IRegister _register;
        private Dictionary<string, object> _componentContainer;
        #endregion

        #region Constructors & Initializer
        public Context()
        {
            Init();
        }

        public Context(IRegister register)
        {
            Init();
            InitRegisterProperty(register);
        }

        private void Init()
        {
            _contextId = Guid.NewGuid();
            _componentContainer = new Dictionary<string, object>();
        }

        private void InitRegisterProperty(IRegister register)
        {
            _register = register;
            _register.CurrentContext = this;
        }
        #endregion

        #region Properties
        public object this[string name]
        {
            get
            {
                if (!_componentContainer.ContainsKey(name))
                    throw new ComponentNotFoundException();
                return _componentContainer[name];
            }
            set
            {
                if (_componentContainer.ContainsKey(name))
                    throw new ComponentAlreadyExistException();
                _componentContainer.Add(name, value);
            }
        }

        public IRegister Register
        {
            get
            {
                return _register;
            }
            private set
            {
                InitRegisterProperty(value);
            }
        }

        public Guid ContextId
        {
            get
            {
                return _contextId;
            }
        }
        public bool QueryExists(string name)
        {
            return _componentContainer.ContainsKey(name);
        }
        public bool RemoveComponent(string name)
        {
            return _componentContainer.Remove(name);
        }

        #endregion
    }


public class DefaultRegister : IRegister
    {
        #region Fields
        IContext _context;
        IObjectInjector _objectInjector;
        #endregion

        #region Constructors & Initializer
        public DefaultRegister()
        {

        }
        public DefaultRegister(IObjectInjector objectInjector)
        {
            _objectInjector = objectInjector;
        }
        public DefaultRegister(IContext context, IObjectInjector objectInjector)
        {
            _context = context;
            _objectInjector = objectInjector;
        }
        #endregion

        #region IRegister 成员
        public IContext CurrentContext
        {
            get
            {
                return _context;
            }
            set
            {
                _context = value;
            }
        }
        public void InjectProperty(string targetName, string propertyName, object property)
        {
            InjectProperty(targetName, propertyName, property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        }

        public void InjectProperty(string targetName, string propertyName, object property, BindingFlags bindingFlags)
        {
            object objTarget = _context[targetName];
            _objectInjector.InjectProperty(objTarget, propertyName, property);
        }

        private IObjectInjector Injector
        {
            get
            {
                return _objectInjector;
            }
            set
            {
                _objectInjector = value;
            }
        }
        #endregion
    }


public class ObjectInjector : IObjectInjector
    {
        #region IObjectInjector 成员
        public void InjectProperty(object target, string propertyName, object property)
        {
            InjectProperty(target, propertyName, property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        }

        public void InjectProperty(object target, string propertyName, object property, BindingFlags bindingFlags)
        {
            object objTarget = target;
            Type type = objTarget.GetType();
            PropertyInfo propertyInfo = type.GetProperty(propertyName, bindingFlags);
            propertyInfo.SetValue(objTarget, property, null);
        }
        public Type GetPropertyType(object target, string propertyName)
        {
            Type type = target.GetType();
            PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            if (pi == null)
                return null;
            return pi.PropertyType;
        }
        #endregion
    }


public class TypeInitializer
    {
        #region make it singleton
        public static readonly TypeInitializer Instance;
        static TypeInitializer()
        {
            Instance = new TypeInitializer();
        }
        private TypeInitializer()
        {
            Init();
        }

        private void Init()
        {
            _basicTypes = GetBasicTypes();
        }
        #endregion

        #region Fields
        private IList<Type> _basicTypes;
        #endregion

        public  object CreateInitObject(Type type)
        {
            if(CheckIsBasicType(type))
            {
                return CreateBasicTypeValue(type);
            }
            else
            {
                return Activator.CreateInstance(type);
            }
        }

        public int TransformStringToInteger(string prop)
        {
            return Int32.Parse(prop);
        }

        #region private Helper Methods
        private IList<Type> GetBasicTypes()
        {
            IList<Type> types = new List<Type>();
            types.Add(typeof(System.Int16));
            types.Add(typeof(System.Int32));
            types.Add(typeof(System.Int64));
            types.Add(typeof(System.String));
            //咱不支持数组
            return types;
        }
        private bool CheckIsBasicType(Type type)
        {
            return _basicTypes.Contains(type);
        }
        private object CreateBasicTypeValue(Type type)
        {
            if (type == typeof(System.String))
                return "";
            return Activator.CreateInstance(type);
        }
        #endregion
    }


public class XmlReadAdapter : IReadAdapter
    {
        #region Fields
        private string _xmlPath;
        private XElement _config;
        #endregion

        #region Constructors & Initializer
        public XmlReadAdapter(string xmlPath)
        {
            _xmlPath = xmlPath;
            _config = XDocument.Load(xmlPath).Element("GhRoot");
        }
        #endregion

        #region IReadAdapter 成员

        public System.Xml.Linq.XElement Config
        {
            get
            {
                return _config;
            }
        }

        #endregion
    }

具体实现代码放到SkyDrive上了,http://qpt2ua.blu.livefilestore.com/y1pJn0xE1aDEE8kAITcT2LgpbjUTX_zjtBTJgyvdHP7Pr1iOV686i8Zu_y-koSE4mZcsrz2YRzx5S-slFOWQPdDloScCFi2MSgw/Demo_IoC.rar?download(不过里面还夹杂了点别的project。。。)

配置文件示例:

Goh.xml

<?xml version="1.0" encoding="utf-8" ?>
<GhRoot>
  <Component name="register" type="GlobalHander.Register.DefaultRegister, GlobalHandler.Context.Impl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <Property name="CurrentContext" ref="applicationContext" />
    <Property name="Injector" ref="objectInjector" />
  </Component>

  <Component name="applicationContext" type="GlobalHandler.Context.Context, GlobalHandler.Context.Impl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <Property name="Register" ref="register" />
  </Component>

  <Component name="objectInjector" type="GlobalHandler.Register.ObjectInjector, GlobalHandler.Context.Impl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </GhRoot>

GohComponent.xml

<?xml version="1.0" encoding="utf-8" ?>
<GhRoot>
  <Component name="entityA" type="TeamTest.Entity, TeamTest, Version=1.0.0.0, Culture=neutral,  PublicKeyToken=null">
    <Property name="a" value="wJiang" />
    <Property name="b" value="124" />
</Component>
  <Component name="entityB" type="TeamTest.Entity, TeamTest, Version=1.0.0.0, Culture=neutral,  PublicKeyToken=null">
    <Property name="a" value="wJiang" />
    <Property name="b" value="124" />
</Component>
  <Component name="entityWrapper" type="TeamTest.Entity2, TeamTest, Version=1.0.0.0, Culture=neutral,  PublicKeyToken=null">
    <Property name="entity" ref="entityA" />
</Component>

  <Component name="entityWrapper2" type="TeamTest.Entity3, TeamTest, Version=1.0.0.0, Culture=neutral,  PublicKeyToken=null">
    <Property name="entity" ref="entityB" />
</Component>


  </GhRoot>