zoukankan      html  css  js  c++  java
  • 使用Unity解耦你的系统—PART3——依赖注入

    继续学习Unity,在前几篇中已经将Unity的使用方法做了一个还算详细的介绍了,主要是如何通过代码及配置文件来注册对象之间的关系、Unity内置所有的生命周期管理使用介绍,及Unity的Register和Resolve的一些高级应用。通过在PART1——为什么要使用Unity?的学习我们知道Unity可以帮我们简化并管理对象之间的关系(也就是前几篇所介绍的),而今天则要介绍Unity的另外一个重要功能——DI(依赖注入)。

    本篇文章将主要介绍:

    1、构造函数注入。

    2、属性注入。

    3、方法注入。

    4、使用配置完成各种注入。

    5、对已经创建对象进行注入。

    一、构造函数注入

        在有些时候,我们所编写的类中的构造函数都会包含对其他对象的引用,如下代码:

    public Subject2(MyClass myClass1,MyClass myClass2)
    {
        myClass1.Name = "班级1";
        myClass2.Name = "班级2";
    }

    可以看到这个构造函数有2个参数,都依赖于MyClass类,如果一般情况下想要调用,我们总是需要实现构建好2个myclass对象,所以相对来说比较麻烦,而如果使用Unity来调用就会方便许多,如下代码:

    container.Resolve<Subject2>();

    仅仅一行既可,Unity容器会自动帮我们构建好所需的依赖对象实例。

    当然这个只是简单的使用,在实际的情况下我们不会这么编写代码,我们不会直接引用对象,而是直接引用接口,这样可以解除代码的耦合性,如下代码:

    public Subject2(IClass myClass1)
    {
        myClass1.Name = "班级1";
    }

    这样Subject2类的构造函数仅仅依赖于IClass接口,并依赖于具体的实现类,这种情况下,如果想调用的话,需要实现注册好对象之间的关系,如下代码:

    public static void ConstructorInjection()
    {
        //container.Resolve<Subject2>();
     
        container.RegisterType<IClass, MyClass>();
        container.Resolve<Subject2>();
    }

    通过Unity容器对象的获取对象时候默认总是获取非命名默认注册对象,但是如果调用Subject2的构造函数参数我想指定具体的对象时怎么办?

    这时就需要使用Dependency这个特性类来解决了,在需要特殊指定的依赖关系的参数上加上Dependency特性,并为Dependency指定好参数name(此name参数表示注册对象关系时所指定的名称),代码如下:

    public Subject2([Dependency("your")]
        IClass classInfo)
    {
        classInfo.Name = "班级1";
    }

    调用代码:

    container.RegisterType<IClass, MyClass>()
        .RegisterType<IClass, YourClass>("your");
    container.Resolve<Subject2>();

    可以看到,我对IClass注册了2个对象,而我在Subject2的构造函数参数上使用了Dependency特性指定了ClassInfo参数获取注册名为“your”的对象。

    现在还有一个问题,就是当一个类存在多个构造函数的时候,我们如何区分哪个构造函数需要实现注入,哪个不需要?

    这时就可以使用InjectionConstructor特性来标识,代码如下:

    [InjectionConstructor]
    public Subject2([Dependency("your")] 
        IClass classInfo)
    {
        classInfo.Name = "班级1";
    }
     
    public Subject2(IClass classInfo1, IClass classInfo2)
    {
        classInfo1.Name = "班级1";
        classInfo2.Name = "班级2";
    }

    Unity只会调用标识了InjectionConstructor特性的构造函数,这样就很好的解决了多构造函数的情况下,Unity调用哪个构造函数。

    二、属性注入

          属性注入和构造函数注入类似,只需在需要注入的属性上增加一个Dependency特性,同样的也可以为Dependency指定一个name参数用来指 定注入属性的具体对象,如下代码,在Subject属性上增加了Dependency特性,来表示这个属性需要注入:

    public class MyClass : IClass
    {
        public MyClass()
        {
        }
     
        public void ShowInfo()
        {
            Console.WriteLine("这个是我的班级");
        }
     
        [Dependency("Subject1")]
        public ISubject Subject { get; set; }
     
        public string Name { get; set; }
     
        public string Description { get; set; }
    }

    具体的调用代码:

    public static void PropertyInjection()
    {
        container.RegisterType<ISubject, Subject1>("Subject1")
            .RegisterType<IClass,MyClass>();
        var classInfo = container.Resolve<IClass>();
        Console.WriteLine(classInfo.Subject.Name);
    }

    这样ClassInfo的Subject属性自动关联到了Subject1类上(完成了属性注入),访问classInfo.Subject.Name可以得到“科目1”。

    三、方法注入

          方法注入同样只需在需要注入的方法上增加一个特性——InjectionMethod既可(其使用方法也和构造注入类似),这样Unity会自动帮我们完成注入,方法注入和构造注入一样,同样可以在方法的参数上指定Dependency特性来指定参数所依赖的注册,下面的类代码中包含了构造注入、属性注入及方法注入,这边集合在一起展示相对来说直观一些:

    public class MyClass : IClass
    {
        public MyClass()
        {
        }
     
        public MyClass(ISubject subjectInfo)
        {
            this.TempSubject1 = subjectInfo;
        }
     
        public void ShowInfo()
        {
            Console.WriteLine("构造注入成功后临时科目1名称:" + this.TempSubject1.Name);
            Console.WriteLine("属性注入成功后临时科目名称:" + this.Subject.Name);
            Console.WriteLine("方法注入成功后临时科目2名称:" + this.TempSubject2.Name);
        }
     
        [InjectionMethod]
        public void Init(ISubject subjectInfo)
        {
            TempSubject2 = subjectInfo;
        }
     
        [Dependency("Subject1")]
        public ISubject Subject { get; set; }
     
        public ISubject TempSubject1 { get; set; }
        public ISubject TempSubject2 { get; set; }
     
        public string Name { get; set; }
     
        public string Description { get; set; }
    }

    具体的调用代码:

    public static void MethodInjection()
    {
        container.RegisterType<ISubject, Subject3>("Subject1")
            .RegisterType<ISubject, Subject4>();
        container.RegisterType<IClass, MyClass>();
     
        var classInfo = container.Resolve<IClass>();
        classInfo.ShowInfo();
    }

    效果图如下:

    pic81

    4、使用配置完成各种注入

    上面所演示的代码都是通过代码来完成对象的注入,下面演示下如何通过配置文件来配置这些注入,具体配置代码如下:

    <!--依赖注入配置,包括构造注入,方法注入和属性注入-->
    <alias alias="IClass" type="UnityStudyConsole.IDemo.IClass, UnityStudyConsole" />
    <alias alias="MyClass" type="UnityStudyConsole.Demo.MyClass, UnityStudyConsole" />
    <alias alias="ISubject" type="UnityStudyConsole.IDemo.ISubject, UnityStudyConsole" />
    <alias alias="Subject3" type="UnityStudyConsole.Demo.Subject3, UnityStudyConsole" />
    <alias alias="Subject4" type="UnityStudyConsole.Demo.Subject4, UnityStudyConsole" />
    <container name="Third">
      <register type="IClass" mapTo="MyClass">
        <constructor>
          <param name="subjectInfo" type="ISubject">
            <dependency name="subjectInfo" type="Subject4"/>
          </param>
        </constructor>
        <method name="Init">
          <param name="subjectInfo" type="ISubject">
            <dependency name="subjectInfo" type="Subject4"/>
          </param>
        </method>
        <property name="Subject">
          <dependency name="Subject1" type="Subject3"/>
        </property>
      </register>
    </container>

    代码如下:

    public static void DIConfiguration()
    {           
        //获取特定配置节下已命名的配置节<container name="Third">下的配置信息
        container.LoadConfiguration("Third");
        var classInfo = container.Resolve<IClass>();
        classInfo.ShowInfo();
    }

    具体的效果是和上面截图中是一样的,只不过这边的依赖注入是通过配置文件来实现的。

    5、对已经创建对象进行注入

          一般来说如果想实现依赖注入需要通过Unity容器来进行对象注册,然后通过Unity容器来获取对象,但是如果对象已经存在(就是不是通过Unity容器来获取的对象),这时如何来通过Unity来实现对已有的对象进行依赖注入呢?

    Unity容器已经为我们提供好了这种情况的解决办法,就是BuildUp方法,看下下面的代码就能明白了:

    public static void BuildUp()
    {
        //事先注册好ISubject和MySubject之间的关系
        //并指定一个名称以方便在接口中的属性上应用[Dependency("Subject1")]特性
        //具体请见IDemo.IClass
        container.RegisterType<ISubject, Subject1>("Subject1");
        IClass classInfo = new MyClass();
        IClass classInfo2 = container.BuildUp<IClass>(classInfo);
        Console.WriteLine(classInfo2.Subject.Name);
    }
    #endregion

    在上面的代码中,已经创建好一个对象实例了,这时只需将这个对象作为参数放入BuildUp中,同时还需指定这个对象实例所实现的接口或父类类型,这样Unity就会自动帮我们将这个已存在对象实例中各种注入。

    需要注意的是,与上面所说3种依赖注入的不同的是,上面3中的依赖注入需要在具体的类中使用InjectionConstructor、 InjectionMethodDependency和特性,而如果对已经存在的对象进行依赖注入,则需要将InjectionConstructor、 InjectionMethod和Dependency写在这个对象实例所实现的接口或父类中,否则会报错!

    到本文为止,Unity的各种常用功能都已经介绍完毕了,各位可以下载下面的源代码进行查看,同时也可以通过Main方法中的各个方法来查看Unity的各种功能的使用:

    pic82

    示例代码下载:点我下载

  • 相关阅读:
    【Java面试题】52 java中会存在内存泄漏吗,请简单描述。
    【Java面试题】51 什么时候用assert。
    JS 生成GUID 方法
    最全html5 meta设置详解 (转)
    前端开发工程师 调试方法合集
    [超级懒人最简单法]iPhone 6 plus 适配切图方法分享(转载文章)
    H5移动前端开发常用高能css3汇总
    前端性能监控方案window.performance 调研(转)
    微信浏览器取消缓存的方法
    H5移动APP开发 细节详解(转)
  • 原文地址:https://www.cnblogs.com/wuxl360/p/5753683.html
Copyright © 2011-2022 走看看