zoukankan      html  css  js  c++  java
  • C# IoC学习笔记

        一、引言

        IoC-Invertion of Control,即控制反转,是一种程序设计思想。

        先初步了解几个概念:

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

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

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

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

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

        二、依赖

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

        class Program
        {
            class BMW
            {
                public string Show()
                {
                    return "宝马";
                }
            }
            class ChinesePeople
            {
                private BMW bmw = new BMW();
                public void Run()
                {
                    Console.WriteLine($"今天开{bmw.Show()}上班");
                }
            }
    
            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.抽象不应该依赖于细节,细节应该依赖于抽象。

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

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

        运行结果如下:

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

        四、控制反转

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

        下面看一个简单的示例(请先添加System.Configuration引用):

        interface ICar
        {
            string Show();
        }
    ICar.cs
        interface IPeople
        {
            void Run(ICar car);
        }
    IPeople.cs
        class BMW : ICar
        {
            public string Show()
            {
                return "宝马";
            }
        }
    BMW.cs
        class ChinesePeople : IPeople
        {
            public void Run(ICar car)
            {
                Console.WriteLine($"今天开{car.Show()}上班");
            }
        }
    ChinesePeople.cs
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
        </startup>
        <appSettings>
            <add key="People" value="LinkTo.Test.ConsoleIoC.ChinesePeople,LinkTo.Test.ConsoleIoC"/>
            <add key="Car" value="LinkTo.Test.ConsoleIoC.BMW,LinkTo.Test.ConsoleIoC"/>
        </appSettings>
    </configuration>
    App.config
        class Program
        {
            static void Main(string[] args)
            {
                #region 反射+配置文件实现Ioc
                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();
                #endregion
            }
        }
    Program.cs

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

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

        运行结果如下:

        五、依赖注入

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

        依赖注入是将对象的创建和绑定转移到被依赖对象的外部来实现,一般使用哪些方法来实现呢?

        方法一:构造函数注入

        class ChinesePeopleConstructor
        {
            private readonly ICar _car;
    
            //依赖注入:构造函数注入
            public ChinesePeopleConstructor(ICar car)
            {
                _car = car;
            }
    
            public void Run()
            {
                Console.WriteLine($"今天开{_car.Show()}上班");
            }
        }
    ChinesePeopleConstructor.cs
        class Program
        {
            static void Main(string[] args)
            {
                #region 依赖注入:构造函数注入
                ICar bmw = new BMW();
                ChinesePeopleConstructor people = new ChinesePeopleConstructor(bmw);
                people.Run();
                Console.Read();
                #endregion
            }
        }
    Program.cs

        方法二:属性注入

        class ChinesePeopleProperty
        {
            //依赖注入:属性注入
            public ICar Car { get; set; }
    
            public void Run()
            {
                Console.WriteLine($"今天开{Car.Show()}上班");
            }
        }
    ChinesePeopleProperty.cs
        class Program
        {
            static void Main(string[] args)
            {
                #region 依赖注入:属性注入
                ICar bmw = new BMW();
                ChinesePeopleProperty people = new ChinesePeopleProperty
                {
                    Car = bmw
                };
                people.Run();
                Console.Read();
                #endregion
            }
        }
    Program.cs

        方法三:接口注入

        interface IDependent
        {
            void SetDependent(ICar icar);
        }
    IDependent.cs
        class ChinesePeopleInterface : IDependent
        {
            private ICar _car;
    
            //依赖注入:接口注入
            public void SetDependent(ICar car)
            {
                _car = car;
            }
    
            public void Run()
            {
                Console.WriteLine($"今天开{_car.Show()}上班");
            }
        }
    ChinesePeopleInterface.cs
        class Program
        {
            static void Main(string[] args)
            {
                #region 依赖注入:接口注入
                ICar bmw = new BMW();
                ChinesePeopleInterface people = new ChinesePeopleInterface();
                people.SetDependent(bmw);
                people.Run();
                Console.Read();
                #endregion
            }
        }
    Program.cs

        六、IoC容器

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

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

        B.管理对象生命周期;

        C.映射依赖关系;

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

        6.1、Unity容器使用

        在上一篇《C# AOP学习笔记》的【使用EntLibPIAB Unity实现AOP(带配置)】中,已经使用Unity容器实现了IoC,让我们再来看看配置文件:

        假如只需要IoC不需要AOP,container是这样子的:

    <configuration>
      <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
      </configSections>
      <unity>
        <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
        <containers>
          <container name="IoCContainer">
            <!--注册匹配规则:前面是完整类型名称,后面是所在的dll名称。-->
            <register type="LinkTo.Test.ConsoleAop.UnityConfigAOP.IUserProcessor,LinkTo.Test.ConsoleAop" mapTo="LinkTo.Test.ConsoleAop.UnityConfigAOP.UserProcessor,LinkTo.Test.ConsoleAop"></register>
          </container>
        </containers>
      </unity>
    </configuration>
    View Code

        从IoC的注册匹配规则可以看出,前面是完整类型名称,后面是所在的dll。这个就比较厉害了,假如一个系统有扩展的功能或者个性要求,只需配置使用新的dll即可,原有系统不需要改代码。这样,除了很好地符合了"开闭原则"外,对于构建一个可配置可扩展的系统,是一个非常厉害的利器。 

        参考自:

        https://www.cnblogs.com/jdzhang/p/7104351.html

        推荐博文:

        基于接口设计三层架构

  • 相关阅读:
    Gson通过借助TypeToken类来解决这个问题
    学习心得
    java反射机制及Method.invoke方法(转载)
    IntentService源码分析
    android中一个app中的activity启动另外一个aar包中的activity
    android 动态加载
    eclispe的快捷键
    android sqlite数据库升级
    [C++] any number to binary (Bit manipulation)
    [C++] Sign and magnitude,Ones' complement and Two's complement
  • 原文地址:https://www.cnblogs.com/atomy/p/12516304.html
Copyright © 2011-2022 走看看