• 依赖注入(IOC)二


    上一章我们讲了构造注入与设值注入,这一篇我们主要讲接口注入与特性注入。

    接口注入

    接口注入是将抽象类型的入口以方法定义在一个接口中,如果客户类型需要获得这个方法,就需要以实现这个接口的方式完成注入。实际上接口注入有很强的侵入性,除了要求客户类型增加前面两种方式所需要的代码外,还必须显示地定义一个新的接口并要求客户类型实现它。

     //定义需要注入ITimeProvider的类型
        interface IobjectWithTimeProvider
        {
            ITimeProvider TimeProvider { get; set; }
        }
        
    
        //通过接口方式实现注入
        public class Client:IobjectWithTimeProvider
        {
            public ITimeProvider TimeProvider { get; set; }
        }

    Unit Test

     [TestClass]
        public class TestClent
        {
            [TestMethod]
            public void TestMethod1()
            {
                ITimeProvider timeProvider = 
                    (new Assembler()).Create<ITimeProvider>();
    
                Assert.IsNotNull(timeProvider);//确认可以正常获得抽象类型实例
    
                IObjectWithTimeProvider objectWithProvider = new Client();
                objectWithProvider.TimeProvider = timeProvider;//通过接口方式注入
    
            }
        }

    随着C#语言的发展,接口注入可以采用与设值注入方式相似的方式实现,而且看上去很“Lamada化”。因为不用真正去实现接口,而是通过泛型参数的方式实现,可以说泛型为C#实现接口注入提供了“新生”。

      /// <summary>
        /// 通过泛型参数实现接口注入
        /// </summary>
        /// <typeparam name="T">待注入的接口类型</typeparam>
       public  class Client<T>:ITimeProvider
           where T:ITimeProvider
        {
           /// <summary>
           /// 与设值方式相似的注入入口
           /// </summary>
           public T Provider { get; set; }
    
           /// <summary>
           /// 类似传统接口注入的实现方式
           /// </summary>
           public DateTime CurrentDate
           {
               get { return Provider.CurrentDate; }
           }
        }

    Unit Test

       [TestMethod]
            public void Test()
            {
                var clietn = new Client<ITimeProvider>()
                {
                    Provider = (new Assembler().Create<ITimeProvider>())
                };
    
                //验证设置方式注入的内容
                Assert.IsNotNull(clietn.Provider);
                Assert.IsNotInstanceOfType(clietn.Provider, typeof(SystemTimeProvider));
    
                //验证注入的接口是否可用
                Assert.IsNotInstanceOfType(clietn.Provider.CurrentDate, typeof(DateTime));
    
                //验证是否满足传统接口注入的要求
                Assert.IsTrue(typeof(ITimeProvider).IsAssignableFrom(clietn.GetType()));
            }

    基于特性的注入方式(Attributer)

    直观上,客户程序可能在使用上做出让步以适应变化,但这违背了依赖注入的初衷,即三个角色(客户对象、Assembler、抽象类型)之中两个不能变,如果在Assembler和客户类型选择,为了客户对象影响最小,我们只好在Assembler上下功夫,因为它的职责是负责组装。反过来讲,如果注入过程还需要修改客户程序,那我们就没有必要去“削足适履”地去用“依赖注入”了。

    因此,为了能通过特性方式完成依赖注入,我们只好在Assembler上下功夫

    (错误的实现情况)

    class SystemTimeAttribute:Attribute,ITimeProvider{…}

    [SystemTime]

    class Client{…}

    相信读者也发现了,这样做虽然把客户类型需要的ITimeProvider通过“贴标签”的方式告诉它了,但事实上又把客户程序与SystemTimeAttribute“绑”上了,他们紧密的耦合在一起了。参考上面的三个实现,当抽象类型与客户对象耦合的时候我们就要用Assembler解耦。

    当特性方式出现类似情况时,我们写一个AtttibuteAssembler不就行了吗?

    还不行,设计上要把Attribute设计成一个通道,出于扩展和通用性的考虑,它本身要协助AtttibuteAssembler完成ITimeProvider的装配,最好还可以同时装载其他抽象类型来修饰客户类型。

    示例代码如下

       [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
           public sealed class DecoratorAttribute : Attribute
           {
            //实现客户类型实际需要的抽象类型的实体类型实例,即待注入客户类型的内容
            public readonly object Injector;
            readonly Type type;
    
            public DecoratorAttribute(Type type)
            {
                if (type == null) throw new ArgumentNullException("type");
                this.type = type;
    
                Injector = (new Assembler()).Create(this.type);
            }
    
            //客户类型需要的抽象对象类型
            public Type Type { get { return this.type; } }
           }
    
        public static class AttributeHelper
    {
            public static T Injector<T>(object target) where T : class
            {
                if (target == null) throw new ArgumentNullException("target");
                return (T)(((DecoratorAttribute[])
                    target.GetType().GetCustomAttributes(typeof(DecoratorAttribute), false))
                    .Where(x => x.Type == typeof(T)).FirstOrDefault()
                    .Injector);
            }
    }
        [Decorator(typeof(ITimeProvider))]
        //应用Attribute,定义需要将ITimeProvider通过它注入
        class Client
        {
            public int GetYear()
            {
                //与其他注入不同的是,这里使用ITimeProvider来自自己的Attribute
                var porvider = AttributeHelper.Injector<ITimeProvider>(this);
                return porvider.CurrentDate.Year;
            }
        }

    Unit Test

         [TestMethod]
            public void Test1()
            {
                Assert.IsTrue(new Client().GetYear() > 0);
            }

     

  • 相关阅读:
    vs2010配置驱动开发
    寒假训练 npuctf_2020_bad_guy(11/250)利用overlap与fastbin attack来篡改fd指针,从而通过stdout达到泄露libc
    寒假训练 [OGeek2019]bookmanager(10/250)
    寒假训练 npuctf_2020_level2(9/250)将heap分配到bss上,从而满足程序条件
    寒假训练 npuctf_2020_level2(8/250)修改ebp链来间接修改返回地址
    Windows XP源码跟踪
    寒假训练 houseoforange_hitcon_2016(7/250)
    寒假训练 jarvisoj_level6_x64(6/250)
    寒假训练 de1ctf_2019_weapon(5/250)
    glibc源码逆向——fwrite函数
  • 原文地址:https://www.cnblogs.com/smiler/p/3271823.html
Copyright © 2020-2023  润新知