SourceCode : https://github.com/toyo0103/Demo_EditTemplate_1
依據前一篇延續,接著我們把焦點放在Application與Service之間的狀況
[![](https://3.bp.blogspot.com/-urWNSzGdlp8/V7O6-tD1woI/AAAAAAAAH6w/z2GwsqBn9hANkslSqNpALNAuRiBIuuI4ACLcB/s400/1.png)](https://3.bp.blogspot.com/-urWNSzGdlp8/V7O6-tD1woI/AAAAAAAAH6w/z2GwsqBn9hANkslSqNpALNAuRiBIuuI4ACLcB/s1600/1.png)
前一篇有提到程式應該要面對**抽象**避免高耦合的情況發生,改一髮進而動全身,所以有用**介面**的方式去處理
[![](https://3.bp.blogspot.com/-O7kXIwvgtDk/V7O7kOH1D0I/AAAAAAAAH60/M49OysRZ7R4LxOLv4UNE9WJVLBBpsICJwCLcB/s400/1.png)](https://3.bp.blogspot.com/-O7kXIwvgtDk/V7O7kOH1D0I/AAAAAAAAH60/M49OysRZ7R4LxOLv4UNE9WJVLBBpsICJwCLcB/s1600/1.png)
然而來看看目前實際的程式狀況 : 直接耦合
[![](https://4.bp.blogspot.com/-csgLbuLY5Dw/V7O9BEEADYI/AAAAAAAAH7A/n6mzb9hYUiksdAHYuQmBXuH6aPO0Sk8-wCLcB/s400/1.png)](https://4.bp.blogspot.com/-csgLbuLY5Dw/V7O9BEEADYI/AAAAAAAAH7A/n6mzb9hYUiksdAHYuQmBXuH6aPO0Sk8-wCLcB/s1600/1.png)
[![](https://2.bp.blogspot.com/-LkVwTAXMQHc/V7O9TcpERgI/AAAAAAAAH7I/18cwMlw6YtQxVKhppexI25lzS--gxIIagCLcB/s400/1.png)](https://2.bp.blogspot.com/-LkVwTAXMQHc/V7O9TcpERgI/AAAAAAAAH7I/18cwMlw6YtQxVKhppexI25lzS--gxIIagCLcB/s1600/1.png)
這邊會談到兩個用來解開這種狀況的觀念
依賴注入 (Dependency Injection)簡稱DI
**控制反轉( **Inversion Of Control)簡稱IOC
先看看Google怎麼解釋控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)
不知道看完有沒有懂的感覺XD,明明都是中文看完卻很想說「先生(小姐)可以講中文嗎?」。 撇開這些專業的術語不看,我們直接開始重構來體會這兩個概念
一、重構開始
抽取介面 避免直接耦合的第一步,抽取介面來隔離實體,既然這邊實際耦合了POIService ,那我們就為它抽取出Interface IPOIService
[![](https://3.bp.blogspot.com/-LrWsJfLH2vU/V7PAEPoqZKI/AAAAAAAAH7Y/VCUD_9YVxnEPfAzFCKpQvHY3jQRkMBD9ACLcB/s1600/1.png)](https://3.bp.blogspot.com/-LrWsJfLH2vU/V7PAEPoqZKI/AAAAAAAAH7Y/VCUD_9YVxnEPfAzFCKpQvHY3jQRkMBD9ACLcB/s1600/1.png)
1 2 3 4 5 6 7 8 9 10 public interface IPOIService { List<POI> Get (string channelID ) ; }
將POIService 掛上IPOIService
[![](https://2.bp.blogspot.com/-YS5Dd3-SnX4/V7PA1QAh0ZI/AAAAAAAAH7g/Av6PbiG5yYgct0SYM1kP_n8mTQTLy0N2ACLcB/s400/1.png)](https://2.bp.blogspot.com/-YS5Dd3-SnX4/V7PA1QAh0ZI/AAAAAAAAH7g/Av6PbiG5yYgct0SYM1kP_n8mTQTLy0N2ACLcB/s1600/1.png)
**讓Application依賴介面**
上一篇遇到這種狀況時,我們會做一個Factory來產生實體,並且封裝在Service層。這次我們不這麼做,改成用依賴注入(DI)的方式來實作,將IPOIService 改成用建構子帶入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class HomeController : Controller { IPOIService _POIService; public HomeController (IPOIService poiService ) { _POIService = poiService; } public ActionResult Index (string id ) { var POIs = _POIService.Get(id); return Json(POIs,JsonRequestBehavior.AllowGet); } }
這邊用的IPOIService 是由外部注入的,實體已經不由HomeController 內部來決定了(不New實體了),也就是上面所提到的 DI 依賴注入 (要用的實體由建構子注入 ),IOC控制反轉 (實體已經由內部控制改由外部控制了 )
接下來面臨的問題是,雖然我們讓實體改成用外部注入的方式,但HomeController在建立的時候是由系統去產生的,它怎麼知道IPOIService 的實體是要注入誰,所以現在執行程式會看到以下結果
[![](https://4.bp.blogspot.com/-0ijdWklCbSk/V7QYVfZnz_I/AAAAAAAAH7w/Bj3Sa8cECEcTJNOsObhzWMd6vMjwUJIggCLcB/s1600/1.png)](https://4.bp.blogspot.com/-0ijdWklCbSk/V7QYVfZnz_I/AAAAAAAAH7w/Bj3Sa8cECEcTJNOsObhzWMd6vMjwUJIggCLcB/s1600/1.png)
這時候就要介紹Unity 這個套件來幫我們解決這個問題了
**
**何謂Unity
[![](https://1.bp.blogspot.com/-PEXphTQycyE/V7QY_Y7zGoI/AAAAAAAAH70/n8FT2stz8PQnOEXWrUSl95ilLNBrmTz1gCLcB/s400/MSUNITY.jpg)](https://1.bp.blogspot.com/-PEXphTQycyE/V7QY_Y7zGoI/AAAAAAAAH70/n8FT2stz8PQnOEXWrUSl95ilLNBrmTz1gCLcB/s1600/MSUNITY.jpg)
MSDN上的Unity套件介紹 : [https://msdn.microsoft.com/en-us/library/ff647202.aspx](https://msdn.microsoft.com/en-us/library/ff647202.aspx)
**
**
其實這個套件說穿了就是在Application啟動時,註冊每個Interface對應的實體是誰,當碰到要注入這個Interface時,Unity就會去找找看你有沒有跟它說對應的實體,如果有,它就把實體產生出來幫你帶入。 那就一步一步做做看邊體會它的意思
**1.安裝Unity套件 **
請在Application專案透過Nuget搜尋Unity安裝**Unity.Mvc**
[![](https://2.bp.blogspot.com/-HhIfl_RkSKA/V7QbZyR_3SI/AAAAAAAAH8M/C4VxtKVrLdUkdbq6eGIXxg0bH9m_A6DpACLcB/s640/1.png)](https://2.bp.blogspot.com/-HhIfl_RkSKA/V7QbZyR_3SI/AAAAAAAAH8M/C4VxtKVrLdUkdbq6eGIXxg0bH9m_A6DpACLcB/s1600/1.png)
**
****
2.註冊Interface應該對應的實體**
**
** 這時候打開App_Start資料夾應該會看到多了一個檔案**UnityConfig.cs**
[![](https://2.bp.blogspot.com/-MCmsg-5dEEY/V7Qb_xiRLFI/AAAAAAAAH8U/3JMMEHww4joUlfQI3HjpW0abcn343O9KwCLcB/s320/1.png)](https://2.bp.blogspot.com/-MCmsg-5dEEY/V7Qb_xiRLFI/AAAAAAAAH8U/3JMMEHww4joUlfQI3HjpW0abcn343O9KwCLcB/s1600/1.png)
打開它會看到以下Code跟註解,教你怎麼使用它
[![](https://2.bp.blogspot.com/-B5FxVSeEW9Q/V7QcPsxDVeI/AAAAAAAAH8Y/lsWGO864ftoAVFsUH3nmO9mB4LL0lOAZwCLcB/s640/1.png)](https://2.bp.blogspot.com/-B5FxVSeEW9Q/V7QcPsxDVeI/AAAAAAAAH8Y/lsWGO864ftoAVFsUH3nmO9mB4LL0lOAZwCLcB/s1600/1.png)
請將**RegisterTypes** 改成以下
```csharp
///
Registers the type mappings with the Unity container.
///
The unity container to configure.
///
There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.
public static void RegisterTypes(IUnityContainer container)
{
//TransientLifetimeManager為這個實體的創建生命週期
//表示執行程式時,每次碰到IPOIService都會去new 一個新的POIService
//Unity有許多生命週期可以使用,可以參考官方的文件
//例如PerResolveLifetimeManager就是本次Request只有New一次實體,如果同個Request碰到多次
//都還是會返回第一次New的實體,直到Reqeust結束才會被消滅掉,Singleton的概念
container.RegisterType<IPOIService, POIService>(new TransientLifetimeManager());
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 關於Unity創建實體的生命週期請參考官方文件 : [https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx](https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx) 註冊好後接著執行看看,會發現程式又活過來了,不會再出現剛剛的錯誤頁面,因為程式已經知道該Interface對應的實體是誰,當由外部注入時,Unity會自己去幫你控制實體的產生並且注入 <div class="separator" style="clear: both; text-align: center;">[![](https://4.bp.blogspot.com/-QgBTpClZA_g/V7QgW_P_VNI/AAAAAAAAH8o/EBBv1BKh0pIIbOKaZoPRQB4Gt-dmnLcmgCLcB/s640/1.png)](https://4.bp.blogspot.com/-QgBTpClZA_g/V7QgW_P_VNI/AAAAAAAAH8o/EBBv1BKh0pIIbOKaZoPRQB4Gt-dmnLcmgCLcB/s1600/1.png)</div> 透過這個重構的實例,我們可以從裡面瞭解到何謂**DI**與**IOC**的概念,但可能會想說,那這樣的好處是什麼? 直接New錯了嗎? 回到前一篇提到的,如果直接New的話也就是高耦合的狀況,如果今天面對的實體改動時,很難避免耦合的地方不會跟著變動,所以我們**依賴抽象**(Interface),用了Unity後其實又牽扯到一個大重點"**職責分離**"。 以後如果有Interface與實體的對應關係,直接就會想到要去**UnityConfig.cs**來改,所有的註冊表在這邊統一管理而且一目了然,如果哪天主管跟你說**POIService**它不喜歡,要重新寫個**NewPOIService**,那我們是不是直接把**NewPOIService實作**<span style="color: #bf9000;">IPOIService</span>後,然後打開**UnityConfig.cs**改成這樣即可 ```csharp container.RegisterType<IPOIService, NewPOIService>(new TransientLifetimeManager());
其他地方連動都不用動,就已經直接將對應的實體給換掉了!!! 統一管理之外,也能達到最小更動原則。
這次的重構就先到這邊~