01-Unity深刻淺出(一)

一. 溫故而知新html

  在開始學習Unity框架以前,有必要溫習一下 【依賴倒置原則】和【手寫IOC】, 由於咱們框架代碼的構建都是基於【依賴倒置原則】的,而Unity框架的核心思想就是IOC和DI,因此有必要了解經過【反射+工廠+配置文件】來實現手寫IOC的原理。android

  廢很少說,簡單提煉一下依賴原則的核心理念:面向接口編程。上層(UI層)不直接依賴於下層,而是依賴於下層對應的接口,下層去實現該接口。數據庫

  經典案例:業務層鏈接數據庫,依賴接口,能夠實現不改代碼輕鬆切換數據庫的目的。編程

  手動建立對象的三個階段:直接new實例化→面向接口編程→將建立對象的交給工廠【手寫IOC】。app

 1         #region 手動建立對象幾個階段
 2             {
 3                 Console.WriteLine("--------------一.複習依賴倒置原則和手寫IOC(簡單工廠+反射+配置文件)----------------");
 4                 //1. 直接建立(須要添加對Service層的引用)
 5                 Console.WriteLine("--------------1. 直接建立----------------");
 6                 AndroidPhone androidPhone1 = new AndroidPhone();
 7                 androidPhone1.Call();
 8 
 9                 //2. 利用接口進行改造(須要添加對Service和Interface層的引用)
10                 Console.WriteLine("--------------2. 利用接口進行改造----------------");
11                 IPhone androidPhone2 = new AndroidPhone();
12                 androidPhone2.Call();
13 
14                 //3. 手寫IOC(只須要添加Interface層的引用,但須要把Service層生成的DLL拷貝到該項目bin下)
15                 Console.WriteLine("--------------3. 利用接口進行改造----------------");
16                 IPhone androidPhone3 = ObjectFactory.CreateObject<IPhone>();
17                 androidPhone3.Call();
18 
19                 //總結:第三個階段基本上已是咱們本身寫容器的最高層次
20                 //下面咱們將藉助微軟的第三方容器Unity來建立
21 
22             }
23             #endregion

依賴倒置原則詳見:http://www.cnblogs.com/yaopengfei/p/7101347.html框架

手寫IOC詳見:http://www.cnblogs.com/yaopengfei/p/6891286.htmliphone

二. Unity深刻淺出ide

 

 (一). Unity介紹函數

 1. Unity的獲取方式學習

   ①:下載網址:http://unity.codeplex.com/ 

      ②:經過Nuget獲取  (目前最新版本爲4.0.1)

2. Unity是什麼?

   Unity是一個IOC容器,用它能夠來建立對象,代替手寫反射,它能夠實現依賴注入(DI),它出自於微軟。.Net平臺下相似的其它IOC框架還有:Spring.Net、AutoFac、Castle等等。

3. IOC的含義

   IOC是一種目標,IOC的含義爲控制反轉,即本來上端(UI層)直接建立或使用的對象,交給第三方容器來裝配和建立。

4.  DI的含義

     DI是一種手段,DI的含義爲依賴注入,Unity提供三種方式分別是:屬性注入、方法注入、構造函數注入。

 (二). 使用Unity來建立對象

 1. 建立對象的步驟

  ①:聲明IUnityContainer容器。

  ②:經過RegisterType註冊新類型 或者 經過RegisterInstance註冊已經存在的類型。

  ③:經過Resove解析對象。

特別注意:註冊類型時,能夠指定名稱來註冊,用於同時建立註冊多個類型時,Resove能夠經過指定的註冊名稱來針對指定解析。

 2.  代碼說明

   ①:代碼結構(後面全部的代碼說明都是基於這個結構的)

 詳細說明:各個類的內容待寫。

 

 ②:直接建立簡單對象(須要添加對Interface層和Service層的引用)

 1  {
 2                 Console.WriteLine("-------------------  二. Unity入門  -------------------");
 3                 Console.WriteLine("-------------------  01-直接建立對象  -------------------");
 4                 IUnityContainer container = new UnityContainer();
 5                 //進行註冊,能夠是:  接口-類、父類-子類、抽象類-子類。
 6                 //container.RegisterType<IPhone, AndroidPhone>();
 7                 //也能夠用實例註冊已經存在的對象
 8                 AndroidPhone android = new AndroidPhone();
 9                 container.RegisterInstance<IPhone>(android);
10                 //解析對象
11                 IPhone phone = container.Resolve<IPhone>();
12                 phone.Call();
13                 //咱們會發現,雖然註冊了AndroidPhone對象,可是該對象中的三個屬性並無實例化,依舊爲空
14                 Console.WriteLine("phone.iHeadphone==null? {0}", phone.iHeadphone == null);
15                 Console.WriteLine("phone.iMicrophone==null? {0}", phone.iMicrophone == null);
16                 Console.WriteLine("phone.iPower==null? {0}", phone.iPower == null);
17  }

 詳解:上述代碼咱們分別經過RegisterType註冊類型、RegisterInstance註冊已經存在的類型,咱們會發現最終生成AndoridPhone中的三個屬性並無被實例化,那麼怎麼實例化裏面的屬性呢?這就涉及到後面的依賴注入了。

  ③. 指定命名來建立對象(須要添加對Interface層和Service層的引用)

 1    {
 2                 Console.WriteLine("-------------------  03-同時建立多個對象  -------------------");
 3                 //能夠經過添加一個參數來區分
 4                 IUnityContainer container = new UnityContainer();
 5                 //註冊類型時(指定命名)
 6                 container.RegisterType<IPhone, AndroidPhone>();
 7                 container.RegisterType<IPhone, ApplePhone>("apple");
 8                 container.RegisterType<IPhone, AndroidPhone>("android");
 9                 //依賴注入
10                 container.RegisterType<IMicrophone, Microphone>();
11                 container.RegisterType<IHeadphone, Headphone>();
12                 container.RegisterType<IPower, Power>();
13                 //解析對象
14                 IPhone iphone1 = container.Resolve<IPhone>();
15                 iphone1.Call();
16                 //解析對象(指定命名)
17                 IPhone iphone2 = container.Resolve<IPhone>("apple");
18                 iphone2.Call();
19                 IPhone iphone3 = container.Resolve<IPhone>("android");
20                 iphone3.Call();
21 
22                 //獲取全部已註冊且指定命名的對象(未指定命名的不能獲取)
23                 var list = container.ResolveAll<IPhone>();
24 
25  }

  詳解:註冊類型時,若是默認不指定命名,則後面的會覆蓋前面的,解析多個對象時,永遠是最後一個註冊的類型的對象。  因此RegisterType指定命名 、Resolve解析對象指定命名普遍用於同時建立多個對象的狀況。

  咱們發現上述代碼有三句是依賴注入的,這個在下面講解。

 (三). 依賴注入的三種形式

  Unity實現依賴注入有三種形式,分別是構造函數注入、屬性注入、方法注入,分別對應三個特性:【InjectionConstructor】、【Dependency】、【InjectionMethod】,加上這三個特性代表構造函數中、屬性中、方法中須要注入對象,使其實例化。

  方式一:屬性注入。

  有WinPhone1類,實現了Iphone接口。其中WinPhone1類中的iMicrophone和iHeadphone兩個屬性上分別添加【Dependency】和【Dependency("fk")】,代表該屬性對應的對象須要注入,其中iHeadphone須要Unity容器"指定命名"來註冊類型。

1  public interface IPhone
2     {
3         void Call();
4         IMicrophone iMicrophone { get; set; }
5 
6         IHeadphone iHeadphone { get; set; }
7 
8         IPower iPower { get; set; }
9     }
View Code
 1    /// <summary>
 2     /// 測試屬性注入的類
 3     /// </summary>
 4     public class WinPhone1 : IPhone
 5     {
 6         [Dependency("fk")]   //表示該屬性須要注入,且須要指定名稱
 7         public IMicrophone iMicrophone { get; set; }
 8         [Dependency]   //表示該屬性須要注入
 9         public IHeadphone iHeadphone { get; set; }
10         public IPower iPower { get; set; }
11 
12         public WinPhone1()
13         {
14             Console.WriteLine("{0}被構造", this.GetType().Name);
15         }
16 
17         public void Call()
18         {
19             Console.WriteLine("{0}打電話", this.GetType().Name); ;
20         }
21     }
View Code
 1                IUnityContainer container = new UnityContainer();
 2                 //方式一: 屬性注入
 3                 Console.WriteLine("----------------------------方式一: 屬性注入 -------------------------------");
 4                 container.RegisterType<IPhone, WinPhone1>();
 5                 //指定命名注入
 6                 container.RegisterType<IMicrophone, Microphone>("fk");
 7                 //普通注入
 8                 container.RegisterType<IHeadphone, Headphone>();
 9                 IPhone phone1 = container.Resolve<IPhone>();
10                 phone1.Call();                
View Code

 

補充說明:咱們發現Microphone和HeadPhone均被構造,說明這兩個類被注入成功。

  方式二:方法注入。

  有WinPhone2類,實現了Iphone接口。其中在WinPhone2中的方法Init1234方法上加【InjectionMethod】特性,表示該方法中有對象須要注入。

 1    public class WinPhone2:IPhone
 2     {
 3         public IMicrophone iMicrophone { get; set; }
 4         public IHeadphone iHeadphone { get; set; }
 5         public IPower iPower { get; set; }
 6 
 7         public WinPhone2()
 8         {
 9             Console.WriteLine("{0}被構造", this.GetType().Name);
10         }
11 
12         public void Call()
13         {
14             Console.WriteLine("{0}打電話", this.GetType().Name); ;
15         }
16 
17         [InjectionMethod]   //方法注入,代表該方法中有對象須要注入
18         public void Init1234(IPower power)
19         {
20             this.iPower = power;
21         }
22     }
1        //方式二:方法注入
2        Console.WriteLine("----------------------------方式二:方法注入 -------------------------------");
3        container.RegisterType<IPhone, WinPhone2>();
4        container.RegisterType<IPower, Power>();
5        IPhone phone2 = container.Resolve<IPhone>();
6        phone2.Call();            

補充說明:咱們發現Power被構造,說明該類被注入成功。

   方式三:構造函數注入(又分四種狀況)

  A.  默認注入。 自動識別參數數量最多的構造函數,表示該構造函數中有對象須要注入。

  B. 指定構造函數。經過在構造函數上加 [InjectionConstructor]特性,表示該構造函數中有對象須要注入。

  C. 指定構造函數且指定注入參數的名稱。經過在構造函數上加 [InjectionConstructor]特性,而且在參數上加 [Dependency("fk")] ,表示該構造函數中的該參數中的對象須要注入。

  D. 指定構造函數且指定注入參數的值(瞭解便可)。

 1     /// <summary>
 2     /// 測試構造函數注入(默認方式)
 3     /// </summary>
 4     public class WinPhone3 : IPhone
 5     {
 6         public IMicrophone iMicrophone { get; set; }
 7         public IHeadphone iHeadphone { get; set; }
 8         public IPower iPower { get; set; }
 9 
10         public WinPhone3(IPower iPower)
11         {
12             Console.WriteLine("{0}被構造", this.GetType().Name);
13         }
14 
15         public void Call()
16         {
17             Console.WriteLine("{0}打電話", this.GetType().Name); ;
18         }
19 
20     }
 1     /// <summary>
 2     /// 測試構造函數注入(指定構造函數)
 3     /// </summary>
 4     public class WinPhone4 : IPhone
 5     {
 6         public IMicrophone iMicrophone { get; set; }
 7         public IHeadphone iHeadphone { get; set; }
 8         public IPower iPower { get; set; }
 9         public WinPhone4()
10         {
11 
12         }
13         [InjectionConstructor]   //指定該構造函數須要被注入
14         public WinPhone4(IPower iPower)
15         {
16             Console.WriteLine("{0}被構造", this.GetType().Name);
17         }
18 
19         public void Call()
20         {
21             Console.WriteLine("{0}打電話", this.GetType().Name); ;
22         }
23 
24     }
 1     /// <summary>
 2     /// 測試構造函數注入(指定參數依賴的註冊名稱)
 3     /// </summary>
 4     public class WinPhone5 : IPhone
 5     {
 6         public IMicrophone iMicrophone { get; set; }
 7         public IHeadphone iHeadphone { get; set; }
 8         public IPower iPower { get; set; }
 9         public WinPhone5()
10         {
11 
12         }
13         [InjectionConstructor]   //指定該構造函數須要被注入且指定注入參數的名稱
14         public WinPhone5([Dependency("fk")]IPower iPower)
15         {
16             Console.WriteLine("{0}被構造", this.GetType().Name);
17         }
18 
19         public void Call()
20         {
21             Console.WriteLine("{0}打電話", this.GetType().Name); ;
22         }
23 
24     }
   /// <summary>
    /// 測試構造函數注入(指定參數值)
    /// </summary>
    public class WinPhone6 : IPhone
    {
        public IMicrophone iMicrophone { get; set; }
        public IHeadphone iHeadphone { get; set; }
        public IPower iPower { get; set; }
        public WinPhone6()
        {

        }
        [InjectionConstructor]   //指定該構造函數須要被注入且指定注入參數的值
        public WinPhone6(IPower iPower,string name)
        {
            Console.WriteLine("{0}被構造", this.GetType().Name);
        }

        public void Call()
        {
            Console.WriteLine("{0}打電話", this.GetType().Name); ;
        }

    }
 1                  //方式三:構造函數注入(又分4中狀況)
 2                 /*
 3                  A. 默認方式
 4                  B. 指定構造函數
 5                  C. 指定參數依賴的註冊名稱
 6                  D. 指定參數值
 7                  */
 8                 Console.WriteLine("-----------------------方式三:構造函數注入(又分4中狀況)----------------------------");
 9                 //A. 默認注入參數最多的構造函數
10                 {
11                     Console.WriteLine("-----------------------A. 默認注入參數最多的構造函數----------------------------");
12                     container.RegisterType<IPhone, WinPhone3>();
13                     container.RegisterType<IPower, Power>();
14                     IPhone iphone = container.Resolve<IPhone>();
15                     iphone.Call();
16                 }
17                 //B. 指定構造函數
18                 {
19                     Console.WriteLine("-----------------------B. 指定構造函數----------------------------");
20                     container.RegisterType<IPhone, WinPhone4>();
21                     container.RegisterType<IPower, Power>();
22                     IPhone iphone = container.Resolve<IPhone>();
23                     iphone.Call();
24                 }
25                 //C. 指定參數依賴的註冊名稱
26                 {
27                     Console.WriteLine("-----------------------C. 指定參數依賴的註冊名稱----------------------------");
28                     container.RegisterType<IPhone, WinPhone5>();
29                     container.RegisterType<IPower, Power>("fk");
30                     IPhone iphone = container.Resolve<IPhone>();
31                     iphone.Call();
32                 }
33                 //D.指定參數值(瞭解便可)
34                 {
35                     Console.WriteLine("-----------------------D.指定參數值(瞭解便可)----------------------------");
36                     container.RegisterType<IPhone, WinPhone6>(new InjectionConstructor(new Power() { }, "ypf"));
37                     IPhone iphone = container.Resolve<IPhone>();
38                     iphone.Call();
39                 }

  小結一下: 以上內容分別介紹了IOC和DI的概念、Unity的使用方式和建立對象的方式、DI的三種注入方式。細心的人或者有經驗的人必定會發現,上述代碼依舊須要添加對Service層和Interface層的引用,那麼用Unity有什麼用?使用了Untiy來建立對象有何好處呢?

  咱們本身手寫IOC的方式(反射+工廠+配置文件)的方式已經能夠脫離對Service層的直接引用了,怎麼引入Unity反而又須要添加對Service層的引用呢?這不是徒增煩惱麼?

  下面咱們來解惑:上述全部代碼(包括Unity步驟的三走)都是在代碼層次上,介紹Unity的基本API的使用,方便咱們你們明白Unity有哪些功能,但在實際開發中,Unity三步走中的第二步,註冊類型是經過配置文件來註冊的,建立Unity容器和解析類型是經過代碼來實現的→→這樣就能夠達到 脫離Service層的引用了(只須要把Service層的dll複製到主程序的bin文件下便可)。

  有的人還會發現:即便使用了配置文件最多也就是和手寫iOC打了個平手,並無看到Unity特有的好處,咱們下個章節將介紹的Unity的聲明週期,便是Unity特有的一些封裝,就能夠見識到Unity的強大。

相關文章
相關標籤/搜索