文章出處

上一章我們講了構造注入與設值注入,這一篇我們主要講接口注入與特性注入。

接口注入

接口注入是將抽象類型的入口以方法定義在一個接口中,如果客戶類型需要獲得這個方法,就需要以實現這個接口的方式完成注入。實際上接口注入有很強的侵入性,除了要求客戶類型增加前面兩種方式所需要的代碼外,還必須顯示地定義一個新的接口并要求客戶類型實現它。

 //定義需要注入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);
        }

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()