zoukankan      html  css  js  c++  java
  • 设计模式のIOC(控制反转)

     一、什么是Ioc

      IoC(Inverse of Control)的字面意思是控制反转,它包括两个内容: 控制、反转

      可以假设这样一个场景:火车运货,不同类型的车厢运送不同类型的货物,板车运送圆木,罐车运送柴油,箱车运送水果。那么对于运送货物这件事,需是列车挂不同的车厢运送货物。显然列车和运送货物之间是有依赖关系的(控制:依赖关系)。我们把列车挂什么样的车厢交给调度中心,而不是交给列车决定,这就形成了依赖反转。

      因为IoC确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级人物Martin Fowler提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了、易于理解。 

    依赖注入和控制反转是同一概念吗?

            根据上面的讲述,应该能看出来,依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

            其实IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC/DI容器来创建并注入它所需要的资源了。

    这么小小的一个改变其实是编程思想的一个大进步,这样就有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

           举个例子:有一个闹钟会问早安,当它地理位置定位在中国的时候,它会问候:早安;当在美国的时候,它会问候:Good Morning;那么我们把闹钟看做一个客户,问候看做一个服务,这个闹钟是依赖于服务的。闹钟遵从OCP原则应该有一个SayMorning的注入点,而问候就需要使用策略模式列出。实现这个功能:

    public interface ISayMorning
    {
    
        void SayMorning();
    }
    
    public class ChinesePosition:ISayMorning
    {
        void SayMorning()
        {
            Console.Writeline("早安!");
        }
    }
    
    public class EnglishPosition:ISayMorning
    {
        void SayMorning()
        {
        Console.Writeline("GoodMorning!");
        }
    }
    
    //下面创建客户
    
    public class ClockClient()
    {
        public ISayMorning SayService{set;}
        public Set_Sayservices(ISayMorning sayService)
        {
            SayService=sayService;
        }
    
        public SayMorning()
        {
           SayService.SayMorning();
        }
    }
    
    //主函数中
    
    var clock=new ClockClient();
    var saysChinese=new ChinesePosition();
    clock.Set_Sayservices(saysChinese);
    clock.SayMorning();

    以上,闹钟说话依赖于说话服务,说话服务有很多策略(算法),我们将服务注入(DL)客户,将客户的依赖项(服务)反转到对象创建后再根据类别注入选择,这就形成了依赖反转(IOC)。这样做的的好处是策略变化(地区),我们只要新建类就好了

    而不用修改已经写好的代码,实现了OCP(设计模式遵循的六大原则之 开闭原则)。

           

    二、几个相似相关的概念

    依赖倒置原则(DIP):一种软件架构设计的原则(抽象概念)。

    控制反转(IoC):一种反转流、依赖和接口的方式(DIP的具体实现方式)。

    依赖注入(DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。

    IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。

    三、Ioc的类型

    IOC的实现有三种方式

    构造注入:就是将开头例子中的Setter方式,在创建客户对象的时候,初始化进入。

    Setter注入:就是开头的例子。

    依赖获取。就是在注入的时候,利用一下虚拟工厂(Abstract Factory),这种适用于服务不仅一种的时候,比如我们赋予闹钟报时功能。

    1、构造函数注入 

    public Class SayHello
    {
        private IPeople _people;
        public SayHello(IPeople  p)
    
        {
            _people=p;
        }
    
       public void Say()
        {
            _people.Say();
    
        }
    
    }

    2、属性注入

    using Microsoft.VisualStudio.TestTools.UnitTesting; 
    using VisionLogic.Training.DependencyInjection.Scenario; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest 
    { 
        [TestClass] 
        public class SetterInjectionTest 
        { 
            class Client 
            { 
                private IWeatherReader reader; 
                public IWeatherReader Reader 
                { 
                    get { return reader; } 
                    set { reader = value; } 
                } 
            }
    
            [TestMethod] 
            public void Test() 
            { 
                IWeatherReader reader = new Assembler<IWeatherReader>().Create(); 
                Client client = new Client(); 
                client.Reader = reader; 
                Assert.IsNotNull(client.Reader); 
            } 
        } 
    }

    也可以写一个Setter方法

    3、接口注入

    using Microsoft.VisualStudio.TestTools.UnitTesting; 
    using VisionLogic.Training.DependencyInjection.Scenario; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest 
    { 
        [TestClass] 
        public class InterfaceInjectionTest 
        { 
            interface IClientWithWeatherReader 
            { 
                IWeatherReader Reader { get; set;} 
            } 
    
            class Client : IClientWithWeatherReader 
            { 
                private IWeatherReader reader; 
    
                #region IClientWithWeatherReader Members 
                public IWeatherReader Reader 
                { 
                    get { return reader; } 
                    set { reader = value; } 
                } 
                #endregion 
            } 
    
            [TestMethod] 
            public void Test() 
            { 
                IWeatherReader reader = new Assembler<IWeatherReader>().Create(); 
                Client client = new Client(); 
                IClientWithWeatherReader clientWithReader = client; 
                clientWithReader.Reader = reader; 
                Assert.IsNotNull(clientWithReader.Reader); 
            } 
        } 
    } 

    4、依赖获取

    public interface ISayMorning
    {
    
        void SayMorning();
    }
    
    public class ChinesePosition:ISayMorning
    {
        void SayMorning()
        {
            Console.Writeline("早安!");
        }
    }
    
    public class EnglishPosition:ISayMorning
    {
        void SayMorning()
        {
        Console.Writeline("GoodMorning!");
        }
    }
    
    public interface IFactory
    {
        ISayTime MakeTimeSayer();
         ISayMoring MakeMorningSayer();
    }
    
    public class FactoryAmerican:IFactory
    {
         public ISayTime MakeTimeSayer()
        {
          return new EnglishPositionTime(); //未实现
        }
          public ISayMoring MakeMorningSayer()
        {
          return new EnglishPosition();
        }
    }
    
    //位于中国的工厂
    public class FactoryChinese:IFactory
    {
         public ISayTime MakeTimeSayer()
        {
          return new ChinesePositionTime(); //未实现
        }
          public ISayMoring MakeMorningSayer()
        {
          return new ChinesePosition();
        }
    }
    
    public statics class FactoryContainer
    {
           static FactoryContainer()
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load("Config.xml");
                XmlNode xmlNode =xmlDoc.ChildNodes[1].ChildNodes[0].ChildNodes[0];
                              
                if ("Chinese" == xmlNode.Value)
                {
                    factory = new FactoryChinese();
                }
                else if ("American" == xmlNode.Value)
                {
                    factory = new FactoryAmerican();
                }
                else
                {
                    throw new Exception("Factory Init Error");
                }
            }
        }
    }
    
    //调用
               IFactory factory = FactoryContainer.factory;
                IWindow window = factory.SayMorning();

    这样,我们可以用过xml文件配置工厂(也就是不同的定位条件)。

    四、一个例子

    C#

    using System; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer 
    { 
        /// <summary> 
        /// 抽象的处理对象 
        /// </summary> 
        public interface IObjectWithGuid 
        { 
            string Guid { get; set;} 
        } 
    }

    定义需要注入的限制接口,并用一个Attribute管理它 
    C#

    using System; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer 
    { 
        /// <summary> 
        /// 需要注入的用以限制最大数量的接口 
        /// </summary> 
        public interface ICapacityConstraint 
        { 
            int Max { get;} 
        } 
    
        public class CapacityConstraint : ICapacityConstraint 
        { 
            private int max; 
            public CapacityConstraint(){this.max = 0;} // 默认情况下不限制 
            public CapacityConstraint(int max) { this.max = max; } 
            public int Max { get { return max; } } 
        } 
    
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 
        public class ConstraintAttribute : Attribute 
        { 
            private ICapacityConstraint capacity; 
            public ConstraintAttribute(int max) { this.capacity = new CapacityConstraint(max); } 
            public ConstraintAttribute() { this.capacity = null; } 
            public ICapacityConstraint Capacity { get { return capacity; } } 
        } 
    } 

    Assembler上增加通过Attribute注入限制的响应。 

    using System; 
    using System.Collections.Generic; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer 
    { 
        public class Assembler 
        { 
            /// <summary> 
            /// 登记相关类型对“最大容量”属性的使用情况 
            /// </summary> 
            private IDictionary<Type, ConstraintAttribute> attributeRegistry = new Dictionary<Type, ConstraintAttribute>(); 
    
            /// <summary> 
            /// 登记每个类型(如须受到“最大容量”属性限制的话),实际已经创建的对象数量 
            /// </summary> 
            private IDictionary<Type, int> usageRegistry = new Dictionary<Type, int>(); 
            public T Create<T>() where T : IObjectWithGuid, new() 
            { 
                ICapacityConstraint constraint = GetAttributeDefinedMax(typeof(T)); 
                if ((constraint == null) || (constraint.Max <= 0)) // max <= 0 代表是不需要限制数量的。 
                    return InternalCreate<T>(); 
                else 
                { 
                    if (usageRegistry[typeof(T)] < constraint.Max) // 检查是否超出容量限制 
                    { 
                        usageRegistry[typeof(T)]++; // 更新使用情况注册信息 
                        return InternalCreate<T>(); 
                    } 
                    else 
                        return default(T); 
                } 
            } 
    
            // helper method 
            // 直接生成特定实例,并setter 方式注入其guid。 
            private T InternalCreate<T>() 
            where T : IObjectWithGuid, new() 
            { 
                T result = new T(); 
                result.Guid = Guid.NewGuid().ToString(); 
                return result; 
            } 
    
            /// helper method. 
            // 获取特定类型所定义的最大数量, 同时视情况维护attributeRegistry 和usageRegistry 的注册信息。 
            private ICapacityConstraint GetAttributeDefinedMax(Type type) 
            { 
                ConstraintAttribute attribute = null; 
                if (!attributeRegistry.TryGetValue(type, out attribute)) //新的待创建的类型 
                { 
                    // 填充相关类型的“最大容量”属性注册信息 
                    object[] attributes = type.GetCustomAttributes(typeof(ConstraintAttribute), false); 
                    if ((attributes == null) || (attributes.Length <= 0)) 
                        attributeRegistry.Add(type, null); 
                    else 
                    { 
                        attribute = (ConstraintAttribute)attributes[0]; 
                        attributeRegistry.Add(type, attribute); 
                        usageRegistry.Add(type, 0); // 同时补充该类型的使用情况注册信息 
                    } 
                } 
                if (attribute == null) 
                    return null; 
                else 
                    return attribute.Capacity; 
            } 
        } 
    } 

    4.2对方案的测试 

    using Microsoft.VisualStudio.TestTools.UnitTesting; 
    using VisionLogic.Training.DependencyInjection.Scenario.Attributer; 
    namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest.Attributer 
    { 
        [TestClass()] 
        public class AssemblerTest 
        { 
            public abstract class ObjectWithGuidBase : IObjectWithGuid 
            { 
                protected string guid; 
                public virtual string Guid 
                { 
                    get { return guid; } 
                    set { guid = value; } 
                } 
            } 
    
            [Constraint(2)] // 通过属性注入限制 
            public class ObjectWithGuidImplA : ObjectWithGuidBase { } 
            [Constraint(0)] // 通过属性注入限制 
            public class ObjectWithGuidImplB : ObjectWithGuidBase { } 
            [Constraint(-5)] // 通过属性注入限制 
            public class ObjectWithGuidImplC : ObjectWithGuidBase { } 
            public class ObjectWithGuidImplD : ObjectWithGuidBase { } 
    
            [TestMethod] 
            public void Test() 
            { 
                Assembler assembler = new Assembler(); 
                for (int i = 0; i < 2; i++) 
                Assert.IsNotNull(assembler.Create<ObjectWithGuidImplA>()); 
                Assert.IsNull(assembler.Create<ObjectWithGuidImplA>()); // 最多两个 
                for (int i = 0; i < 100; i++) 
                Assert.IsNotNull(assembler.Create<ObjectWithGuidImplB>()); // 不限制 
                for (int i = 0; i < 100; i++) 
                Assert.IsNotNull(assembler.Create<ObjectWithGuidImplC>()); // 不限制 
                for (int i = 0; i < 100; i++) 
                Assert.IsNotNull(assembler.Create<ObjectWithGuidImplD>()); // 不限制 
            } 
        } 
    } 
  • 相关阅读:
    error C2955: “std::xx”: 使用 类 模板 需要 模板 参数列表
    打印windows环境变量
    自动化测试覆盖率要到多少才算足够
    每个离谱的bug背后总有一个精彩绝伦的幕后故事
    测试是整个开发过程的瓶颈吗
    如何用指标去度量bug本身的质量情况
    python : 将txt文件中的数据读为numpy数组或列表
    Baxter+Kinect: Robot Teleoperation via Skeleton Tracking and Inverse Kinematics
    安卓环境和配置
    Qt开发经验小技巧181-185
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/7660393.html
Copyright © 2011-2022 走看看