DI Anti Patterns - Service Locator

前言: 在前一篇的 Anti-Pattern 中我們介紹了最經典的反模式:Control Freak 。而在這篇我們要介紹的另外一個常見的反模式可以說是他的進化版本,尤其是這個模式是知名的架構師 Martin Fowler 提出的。不過在數年後有人挑戰了這個想法,該文章的討論串比正文還要長,但思辨的過程還是值得一看。 常見類型 Anti-pattern Description Control Freak 依賴反轉(Inversion of Control)的對立面,直接控制實體 Service Locator 使用一個內隱服務提供依賴給呼叫端 Ambient Context 使用靜態存取子,提供單一的依賴 Constrained Construction 讓建構子有一個特定的方法簽章 Service Locator 定義 是一種軟體開發中的設計模式,通過一個集中註冊表(通常會是 Singleton)來處理請求並返回處理特定任務所需的必要訊息。最常見的實做方式通常就是利用 Static Factory 來實做這個註冊表。而系統中可以在系統進入點以外的地方呼叫這個註冊表的方法取得指定的實做。 表準案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class HomeController : Controller { public HomeController() { } public ViewResult Index() { IProductService service = Locator.GetService<IProductService>(); var products = service.GetProducts(); return this.View(products); } } 1 2 3 4 5 6 7 8 9 10 11 public class ProductService : IProductService { private readonly IProductRepository repository; public ProductService() { this.repository = Locator.GetService<IProductRepository>(); } public IEnumerable<Product> GetProducts() { return this.repository.GetProducts(); } } 這邊可以看到當要使用 IProductService 時,就呼叫註冊表的方法來取得實做,但這個依賴關係因為不是在建構時給予因此從外部會不容易知道他們兩者之間會有這樣的關係存在。另外這個作法同樣也會遇到 Static Factory 會遇到的問題,只是 Service Locator 將原本呼叫與依賴關係建立的那一層做了一個包裝。原本的 Static Factory 會與實做相同介面的類別緊緊的綁在一個 Factory 類別中,而包裝起來之後變成所有類型的依賴與實做會都綁在 Service Locator 這個類別裡,它變得與更多依賴有關。同時,在使用上每個需要取得依賴的地方都需要能存取 Service Locator 。 ...

February 17, 2019 · 2 分鐘 · 373 字

DI Patterns

前言 這篇文章主要講的是 DI 常見的方式,共四種模式這邊首先介紹最為常見的建構子注入。其實 DI 這個技巧我們常常都會使用到,甚至就算你不知道也會用它,雖然是什麼相當偉大的技巧但是卻是很實用也很重要的技巧。DI(Dependency Injection , 依賴注入),講的是針對抽象來撰寫程式而非實體。底下會有範例來展示這項技巧。另外,這篇文章主要是依照 Dependency Injection in .NET 這本書的內容來撰寫的,但有部分觀點屬於個人。 內文 Constructor Injection (建構子注入) 這個模式應用很廣泛也是很容易實現的一個DI模式,在使用這個模式時會需要類別提供一個公開(public)並且需要一個實體作為參數的的建構子,通常的情況下這個類別只會有這麼一個建構子的存在,另外,請儘量避免overload建構子,保持只有單一一個建構子的情況。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //實現 Constructor Injection 的類別 public class MyFirstDIClass { // DI 欄位是readonly可以在類別初始化後避免被修改。 private readonly AnimalRepository repository; public MyFirstDIClass(AnimalRepository repository) { // 確保注入的實體是存在的 if(repository == null) { throw new ArgumentNullException("repository"); } this.repository=repository;// 注入依賴的實體 } } 通常所要求輸入的參數會是一個抽象類別或是一個介面(abstract class or interface),之所以使用這兩種 Type 是因為建構子可以不用去管真正實現的類別究竟為何,只要這個類別繼承或是實作指定的 Type 即可,因為對於這個注入的實體也只會使用到指定 Type 所提供的公開方法而已。 在使用這個方法時有個需要注意的地方,要儘量避免在建構子中撰寫其他的邏輯。建構子裡面最好就是只有建構這個類別時所需要的邏輯即可,所有初始化這個類別以外的邏輯都不應該出現在這裡,簡單來講就是維持建構子的單純。 一般來說 Constructor Injection 這個方式的優缺點為 ...

October 20, 2013 · 1 分鐘 · 143 字

Simple usage of Reflection and Dependency Injection

前言 這一篇主要是要記錄一下反射(Reflection)的方便用法,作為以後的備忘。底下的範例會順便用一些Depency Injection的觀念來實作。 根據MSDN的描述 反映 (Reflection) 會提供 Type 型別的物件,用來描述組件、模組和型別。 您可以使用反映來動態建立型別的執行個體、將型別繫結至現有物件,或從現有物件取得型別,並叫用其方法或存取其欄位和屬性。 如果您在程式碼中使用屬性,反映可讓您存取這些屬性。 情境 在動物園裡面有很多不同的動物園區,每個動物園區都可以看到專屬於該園區的動物。當遊客買票進去時,可以依據票的種類去不同的園區觀賞。假設目前我們有兩個園區: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /// 獅子王動物園區 public class LionKing { private ICollection<string> _Pool; public LionKink() { _Pool = new List<string>{ "Simba", "Nala", "Pumbaa", "丁滿" }; } public void SeeAnimals() { foreach(var name in _pool) Console.WriteLine("在動物園區我看到 {0}."name); } } /// 憤怒鳥動物園區 public class AngryBird { private ICollection<string> _Pool; public AngryBird() { _Pool = new List<string> { "RedBird", "BlueBird", "YellowBird" }; } public void SeeAnimals() { foreach(var name in _pool) Console.WriteLine("在動物園區我看到{0}."name); } } 方法: 通常看到這樣的需求有一種寫法是最簡單直覺的: ...

August 30, 2012 · 3 分鐘 · 473 字