zoukankan      html  css  js  c++  java
  • 浅谈IOC

    一、引言

    IOC-Invertion of Control,即控制反转,是一种程序设计思想,世上本没有路,走的人多了便有了路,本文将一步步带你了解IOC设计思想的演进之路。

    在学习IOC之前我们先初步了解几个概念

    依赖(Dependency):就是有联系,表示一个类依赖于另一个类

    依赖倒置原则(DIP):设计模式六大原则之一,是一种软件架构设计原则

    控制反转(IOC):一种软件设计原则,上层对下层的依赖(即底层模块的获得)交给第三方

    依赖注入(DI):实现IOC的一种方式、手段

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

    二、依赖

    依赖就是有联系,有地方使用它就是有依赖它,下面看一个简单的示例

      class BMW
        {
            public string Show()
            {
                return "宝马";
            }
        }
        class ChinesePeople
        {
            private BMW bmw = new BMW();
            public void Run()
            {
                Console.WriteLine($"今天开{bmw.Show()}上班");
            }
        }
         class Program
        {
            static void Main(string[] args)
            {
                ChinesePeople people = new ChinesePeople();
                BMW bmw = new BMW();
                people.Run();
                Console.Read();
            }
        }
    View Code

    上面中国人开着宝马去上班,客户端有使用中国人、宝马汽车两个对象,中国人中有使用对象宝马汽车,我们可以从中找到三个依赖关系:

    客户端依赖对象ChinesePeople;

    客户端依赖对象BMW;

    ChinesePeople依赖对象BMW;

    三、依赖倒置原则

    过些日子来了新需求,中国人不仅要开宝马去上班,还要开奔驰去上班,如果按照上面直接依赖关系的方式去做,我们就需要修改ChinesePeople类,让它实现一个参数为宝马的重载方法Run(),显然这样不是好的设计,我们总不能每次新增一种汽车(即修改下层模块)都要去修改ChinesePeople类吧(相对于汽车为上层模块),太麻烦了。。。

    先简单分析一下,耦合关系就是依赖关系,如果依赖关系很重,牵一发而动全身,将很难维护扩展,耦合关系越少,系统会越稳定,因此要较少依赖

    定义:A.高层模块不应依赖于底层模块,两者应该依赖于抽象

       B.抽象不应该依赖于细节,细节应该依赖于抽象

    在这个图中,我们发现高层模块定义接口,将不直接依赖于下层模块,下层模块负责实现高层模块定义的接口,下面看代码demo:

        interface ICar
        {
            string Show();
        }
        class BMW:ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
         class BenZ : ICar
        {
            public string Show()
            {
                return "奔驰";
            }
        }
        interface IPeople
        {
             void Run(ICar bmw);
        }
        class ChinesePeople :IPeople
        {
            public void Run(ICar bmw)
            {
                Console.WriteLine($"今天开{bmw.Show()}上班");
            }
        }
         class Program
        {
            static void Main(string[] args)
            {
                ICar carBMW = new BMW();
                ICar carBenZ = new BenZ();
                IPeople people = new ChinesePeople();
                people.Run(carBMW);
                people.Run(carBenZ);
                Console.Read();
            }
        }
    View Code

    分析:上面代码中,ChinesePeople类不再依赖于具体的汽车,而是依赖于汽车的抽象,这样使得不管换什么样的汽车品牌,中国人都是可以开着去上班的,而且不需要修改ChinesePeople类。想一下,这样是不是挺好的,我们可以得出:上层不再依赖细节,相比面向实现,面向接口较好,因为抽象相比细节要更稳定。

    四、控制反转

    上面示例中,我们实现了具体的人和具体的汽车的隔离,具体人只和汽车的接口有关。但是Program中main方法里的具体对象写死了,控制权变小,当我要修改美国人开着福特去上班时,就不得不要去修改代码,那怎么把控制权转移呢?

    下面看一个简单的示例:

        interface ICar
        {
            string Show();
        }
        class BMW:ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
        interface IPeople
        {
             void Run(ICar bmw);
        }
        class ChinesePeople :IPeople
        {
            public void Run(ICar bmw)
            {
                Console.WriteLine($"今天开{bmw.Show()}上班");
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                string people =     ConfigurationManager.AppSettings["people"];
                string car = ConfigurationManager.AppSettings["car"];
                Assembly assemblypeople = Assembly.Load(people.Split(',')[1]);
                Assembly assemblycar = Assembly.Load(car.Split(',')[1]);
                Type typepeople = assemblypeople.GetType(people.Split(',')[0]);
                Type typecar = assemblypeople.GetType(car.Split(',')[0]);
                IPeople ipeople= (IPeople)Activator.CreateInstance(typepeople);
                ICar icar = (ICar)Activator.CreateInstance(typecar);
                ipeople.Run(icar);
                Console.Read();
            }
        }
    
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
        </startup>
      <appSettings>
        <add key="people" value="MyIOC_IOC.ChinesePeople,MyIOC_IOC"/>
        <add key="car" value="MyIOC_IOC.BMW,MyIOC_IOC"/>
      </appSettings>
    </configuration>
    View Code

    上面代码中,我们使用反射+配置文件的方式,将对象创建的控制权转移到了配置文件,这就是所谓的控制反转

    分析,控制反转是将对象创建的控制权交给了第三方,可以是IOC容器,它就相当于工厂,我们要什么对象,工厂给我们什么对象,这样依赖关系就变了,它们(人和车)都依赖于IOC容器,通过IOC容器建立它们之间的依赖关系。(依赖对象不再被依赖模块的类中直接通过new来获取)

    五、依赖注入

    上面说到的控制反转,我们了解到是将控制权转移,这是我们的目的,配置文件+反射是是一种实现,而依赖注入则提供的是一种思想,或者说是实现IOC的手段。

    依赖注入是将对象的创建和绑定转移到被依赖对象的外部来实现。在依赖关系中ChinesePeople类所依赖的对象BMW类的创建和绑定是在ChinesePeople类内部执行的,显然这种方法是不可取的,那我们怎么BMW类的引用传递给ChinesePeople类呢?

    方法一 构造函数注入

         interface ICar
        {
            string Show();
        } 
        class BMW:ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
         class ChinesePeopleContructor 
        {
            private ICar _car;
            public ChinesePeopleContructor(ICar bmw)
            {
                _car = bmw;
            }
            public void Run()
            {
                Console.WriteLine($"今天开{_car.Show()}上班");
            }
        }
        static void Main(string[] args)
            {
                ICar car = new BMW();
                ChinesePeopleContructor people = new ChinesePeopleContructor(car);
                people.Run();
                Console.Read();
            }
    View Code

    分析,BMW类对象的创建和绑定转移到ChinesePeople类的外部来实现,解除了两个对象之间的耦合,当需要开奔驰去上班的时候,只需要定义一个奔驰类,外部重新绑定依赖,不需要修改ChinesePeople类的内部,即可是先中国人开奔驰去上班的需求

    方法二 属性注入

    interface ICar
        {
            string Show();
        }
        class BMW:ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
        class ChinesePeopleProperty
        {
            private ICar _ICar;
            public ICar IC
            {
                get { return _ICar; }
                set { _ICar = value; }       
            }
            public void Run()
            {
                Console.WriteLine($"今天开{_ICar.Show()}上班");
            }
        }
        static void Main(string[] args)
         {
             ICar car = new BMW();
             ChinesePeopleProperty people = new ChinesePeopleProperty();
             people.IC = car;
             people.Run();
             Console.Read();
         }
    View Code

    分析,属性注入是通过给属性赋值,从而传递依赖

    方法三 接口注入

         interface ICar
        {
            string Show();
        }
         class BMW:ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
         interface IDependent
        {
            void SetDependent(ICar icar);
        }
        class ChinesePeopleInterface : IDependent
        {
            private ICar _ICar;
            public void SetDependent(ICar icar)
            {
                _ICar = icar;
            }
            public void Run()
            {
                Console.WriteLine($"今天开{_ICar.Show()}上班");
            }
        }
            static void Main(string[] args)
            {        
                  ICar car = new BMW();
                  ChinesePeopleInterface people = new ChinesePeopleInterface();
                  people.SetDependent(car);
                  people.Run();
                  Console.Read();    
            }      
    View Code

    分析,接口依赖是定义一个设置依赖的方法,然后被依赖类继承并实现这个接口

    六、IOC容器

    IOC容器是一个DI框架,主要功能有一下几点

    1.动态创建、注入依赖对象;

    2.管理对象生命周期

    2.映射依赖关系

    常见的IOC容器:Spring.NET,Castle Windsor, Ninject,Autofac,Unity等等。。。

    ioc容器提供了很多丰富的API,由于时间和篇幅等关系,我会在下篇博客中和您一起学习IOC容器之一Unity,敬请期待,未完待续。。。

    不努力一把,坐井观天,将永远不知道自己和别人的差距有多大,身为菜鸟的我相信,天道酬勤,大道至简,最好的成功之道便是坚持、学习、总结。

    本文版权归作者和博客园共有,欢迎转载,转载请注明出处。感谢您的阅读。

  • 相关阅读:
    使用MyBatis时接收值和返回值选择Map类型或者实体类型
    如何在开发时部署和运行前后端分离的JavaWeb项目
    6月22日项目CodeReview问题及总结
    【转载】CSRF攻击及其应对之道
    【转载】MySQL事务以及SELECT ... FOR UPDATE的使用
    win10打开SQLServer, SQL Server 配置管理器
    iframe标签和frame标签异同
    8款让你耳目一新的软件开发工具
    快速开发平台的比较
    CSDN:Java Web 开发平台 WebBuilder 专访
  • 原文地址:https://www.cnblogs.com/jdzhang/p/7104351.html
Copyright © 2011-2022 走看看