前言
記得幾年以前還在當小小碼農的時候,曾經無數次為了公司趕上線寫過類似這樣的程式
- Television.cs
1 | public class Television |
- Person.cs
1 | public class Person |
而出社會寫了幾年程式之後,你開始會耳聞 IoC
,DI
這樣的字眼出現,但礙於當時程度還不夠,因此沒有辦法對這樣的詞產生什麼感覺,今天就透過這篇文章講解什麼是 IoC
,DI
,以及我們怎麼樣使用 IoC
的技巧避免程式 寫得太黏 。
什麼是耦合? (What is coupling)
以前言所描述的例子來說,就是一個耦合的例子。
搭配著上圖來看,ServiceB
直接在 ClassA
當中建立實體,就跟例子中的 Television
類別直接在 Person
類別中被實體化,並且使用其中的方法。這些都會造成耦合的情況。
如何減少耦合? (How to loose coupling)
既然我們知道所謂的耦合就像是上面這種你泥中有我我泥中有你的狀況,我們應該採取策略來避免這些情形,以下將會介紹兩種解開耦合的方式
- 服務定位器 Service Locator
- 依賴注入 Dependency Injection
控制反轉 (Inversion of Control)
控制反轉實際上是一種程式設計的風格,它的目的是為了要降低模組之間的耦合度,而以下的 Service Locator Pattern
以及 Dependency Injection
就是實現 IoC 概念的方式。
服務定位器模式 (Service Locator Pattern)
第一個方法是 服務定位器
模式
由上圖可以知道 ClassA
改使用服務定位器來取得想要得到的實體,他並不需要知道實際上 ServiceA
或 ServiceB
是怎麼樣實作或是怎麼樣產生的,顧名思義,Service Locator 的作用就是幫助我們定位到我們要使用的 Service,然後回傳給我他的實體。
以下簡單的實作一個簡易版的 Service Locator
- ServiceLocator.cs
1 | public static class ServiceLocator |
我們透過 ServiceLocator
的靜態類別,來幫我們做到註冊以及取得實體的動作,大致上是使用到一個 Dictionary
的資料結構,幫助我們可以使用 Key
來取得相應的實體,在這個例子當中,我們就把介面當作 Key
,把實作當成 Value
,讓我們可以簡單的取得要建立實體的型別,最後直接把字典中相應的實體回傳給使用到他的類別。
- Person.cs
1 | public class Person |
然後調整一下我們的 Person
類別,將原本直接耦合的地方透過 ServiceLocator
來幫我們產生所需要的實體,而且這個過程當中並不與實際的 Television
類別有直接的相依,
- Program.cs
1 | void Main() |
最後先在 Program.cs
當中註冊介面以及實作該介面的類別,然後馬上來手動建立 Person
類別的實體,並且呼叫其中的方法看看結果如何。
1 | 電視機打開了 |
相依性注入 (Dependency Injection)
上面的作法雖然抽開了 Person
與 Television
的相依關係,但是反而造成 Person
類別直接相依 ServiceLocator
,有沒有更漂亮的方式可以解決這個問題呢?
再提出解法之前我們先來看我們期望最後程式會長什麼樣子
- Person.cs
1 | public class Person |
上面這樣的解法,才能夠真的算得上解開類別的直接相依問題,因為我們透過 DI 框架建構式注入的方式,自動將實體注入給 television
的建構式參數,並且賦值給私有的 _television
欄位,以下就來看 DI 要怎麼做到這件事情。
這邊使用 Autofac 作為範例,其他的 DI 框架概念亦相去不遠,讀者可自行研究其他的 DI 框架
- Program.cs
1 | void Main() |
來看看這段程式會產生怎樣的結果
1 | 電視機打開了 |
由此可知,在 DI 框架的幫忙之下,你根本不需要知道誰實作了 ITelevision
介面,而且你也不會和 ServiceLocator
這類的靜態類別產生相依。
換言之,假設今天你有另外一台電視類別叫做 SmartTelevision
,同樣可以開電視、關電視、切換頻道,只是做這些動作的方式不一樣,這時候你可以在 DI 註冊的時候輕易的將 Television
類別改為 SmartTelevision
類別,執行之後就完全得到不一樣的結果,相較於一開始的版本,是不是更具彈性了呢?
參考連結
- Dependency Injection 筆記 (6) - Huan-Lin 學習筆記
- Service Locator 模式 - 匠心十年
- Service Locator - GameprogrammingPatterns.com
- Service Locator is an Anti-Pattern - Mark Seemann
- 深入理解DIP、IoC、DI以及IoC容器 - 刘皓(.NET开发)
- Autofac Documentation
- What’s the difference between the Service Locator and the Factory Design pattern?