【简译】关于依赖反转原则、控制反转和依赖注入的抽象的初学者指南
原文在此。
======================================分割線====================================
介紹
文章以介紹依賴反轉原則開始,接下來介紹如何使用控制反轉來實現依賴反轉原則,最后將闡述什么是依賴注入和如何實現它。
背景
在我們開始講依賴注入前,首先要了解依賴注入要解決的問題。為了理解這個問題,我們需要知道兩個事情:一,依賴反轉原則;二,控制反轉(Inversion of Controls(IoC)。我們先討論依賴反轉原則然后講IoC。一旦完成這兩個,我們就可以更好地理解依賴注入,然后我們就可以一窺依賴注入的細節。最后我們討論如何實現依賴注入。
依賴反轉原則
依賴反轉原則是一個指導我們寫出松耦合類的軟件設計原則。根據依賴反轉原則的定義:
1. 高層模塊不應該以依賴低層模塊。他們都應該依賴抽象層。
2.抽象層不應該依賴細節,細節應該依賴抽象層。
這個定義是什么意思?它想傳達什么?讓我們通過例子理解這個。幾年前我被委派寫一個本應該運行在web server的windows service。這個service唯一的需求是無論何時在IIS應用程序池出現錯誤就要在事件日志上登記消息。我們團隊最初的實現是生成兩個類。一個監視這個應用程序池,另一個寫在事件日志里寫消息。我們的類看起來像這樣:
class EventLogWriter {public void Write(string message){//Write to event log here } }class AppPoolWatcher {// Handle to EventLog writer to write to the logsEventLogWriter writer = null;// This function will be called when the app pool has problempublic void Notify(string message){if (writer == null){writer = new EventLogWriter();}writer.Write(message);} }它看起來很好,但是有問題,這個設計違反了依賴反轉原則。例如,高層模塊 AppPoolWatcher?依賴EventLogWriter ,EventLogWriter?不是一個抽象而是一個具體類。讓我告訴你我們收到的關于這個service的下一個需求,問題就變得明朗。
下一個需求是向網絡管理員的email發送關于指定的錯誤的郵件。一個想法是生成一個發送郵件的類并且把他的handle放到?AppPoolWatcher?但是在任意時刻我們只能使用一個對象,要么是?EventLogWriter 要么是?EmailSender?。
一旦我們有更多可選的動作,像發送SMS,問題就變得更加嚴重。那樣,我們就不得不添加一個實例保存在?AppPoolWatcher?的類。依賴反轉原則告訴我們,我們需要解耦這個系統,方法是高級模塊在我們的例子中是?AppPoolWatcher?將依賴一個簡單的抽象并使用它。這個抽象反過來將被映射到一些會執行實際操作的具體類上。
控制反轉
依賴反轉是原則,他只是指明兩個模塊如何相互依賴。控制反轉來實現他。控制反轉使得我們可以使高層模塊依賴抽象而不是底層模塊的具體實現。
為了使用控制反轉實現上述問題方案,第一步,我們需要創建一個抽象讓高層模塊可以依賴。我們創建一個接口來提供這個抽象。這個接口實現從 AppPoolWatcher?收到的通知。
?
public interface INofificationAction {public void ActOnNotification(string message); }?
修改高層模塊來使用這個抽象
class AppPoolWatcher {// Handle to EventLog writer to write to the logsINofificationAction action = null;// This function will be called when the app pool has problempublic void Notify(string message){if (action == null){// Here we will map the abstraction i.e. interface to concrete class }action.ActOnNotification(message);} }低層模塊如何修改,我們需要實現上面的接口在這個類中使得他符合抽象。
class EventLogWriter : INofificationAction { public void ActOnNotification(string message){// Write to event log here } }如果我要發送email和sms,這些類只要實現相同的接口就可以了:
class EmailSender : INofificationAction {public void ActOnNotification(string message){// Send email from here } }class SMSSender : INofificationAction {public void ActOnNotification(string message){// Send SMS from here } }類的最終設計像下圖一樣:
這里,我們已經反轉了控制去符合依賴反轉原則。現在我們的高層模塊只依靠抽象,這正是依賴反轉原則表明的。
但仍然有問題。觀察AppPoolWatcher,我們可以看到雖然它使用接口作為抽象但是我們創建了具體類型并賦值給了這個抽象。為了解決這個問題,我們可以:
class AppPoolWatcher {// Handle to EventLog writer to write to the logsINofificationAction action = null;// This function will be called when the app pool has problempublic void Notify(string message){if (action == null){// Here we will map the abstraction i.e. interface to concrete class writer = new EventLogWriter();}action.ActOnNotification(message);} }但是我們有回到原點了。具體類的創建還是在高層類里。依賴注入進入眼簾。是時候介紹依賴注入的細節了。
依賴注入
依賴注入主要是注入具體實現到一個使用抽象例如接口的類里面。他的主要思想是減少類之間的耦合和移動抽象和具體實現之間的綁定到依賴雷的外面。
有三個方式實現依賴注入:
?Constructor injection(構造函數注入)
在這個方法里,我們傳遞具體類對象到依賴類的構造函數里。在依賴類中我們需要一個可以獲取具體類對象的構造函數并把這個具體類賦值給這個類正在使用的接口handle,用此方法實現構造函數注入。代碼如下:
class AppPoolWatcher {// Handle to EventLog writer to write to the logsINofificationAction action = null;public AppPoolWatcher(INofificationAction concreteImplementation){this.action = concreteImplementation;}// This function will be called when the app pool has problempublic void Notify(string message){ action.ActOnNotification(message);} }在上面的代碼中,構造函數將接受具體類對象并和接口handle綁定。如果我們要傳EventLogWriter的具體實現到這個類中,我們要:
如果像發送email或SMS,我們只需要傳遞各自類對象到AppPoolWatcher的構造函數里。當我們知道這個依賴類的實例將在整個生命期一直使用一樣的具體類,這個方法是有用的。
Method Injection
如果我們想傳遞單獨的具體類都每個方法調用中,我們只需要傳遞依賴到這個方法里。
在方法注入里,我們傳具體類對象到實際調用這個動作的依賴類的方法里。我們需要這個動作函數接受具體類對象的實參并賦值個這個類正在使用接口handle同時調用這個動作。代碼如下:
如果我們要傳EventLogWriter的具體實現到這個類,只要:
EventLogWriter writer = new EventLogWriter(); AppPoolWatcher watcher = new AppPoolWatcher(); watcher.Notify(writer, "Sample message to log");?
如果像發送email或SMS,我們只需要像上面一樣傳遞各自類對象到AppPoolWatcher的調用方法里。
property injection
?
如果具體類的選擇的責任和調用的方法不在一個地方。我們需要屬性注入
在這個方式里,我們通過依賴類暴露的setter屬性傳遞具體類對象。我們通過使用在依賴類的setter屬性或函數來獲取具體類對象并賦值給這個類正在使用的接口handle。代碼如下:
class AppPoolWatcher {// Handle to EventLog writer to write to the logsINofificationAction action = null;public INofificationAction Action{get{return action;}set{action = value;}}// This function will be called when the app pool has problempublic void Notify(string message){ action.ActOnNotification(message);} }如果要傳EventLogWriter具體實現到這個類中,我們只需:
如果像發送email或SMS,我們只需要像上面一樣傳遞各自類對象到AppPoolWatcher暴露的setter中里。當選擇的具體實現的職責和調用的完成的動作在不同的位置/模塊。
在沒有支持屬性的語言里,有單獨的函數設置依賴。這個方法叫做setter 注入。需要注意的是,有可能有些人已經創建了依賴類(dependent class),但是沒有人設置具體類依賴(dependency)。如果我們試圖調用在這種情況下調用這個動作,那我們應該要么有一些映射到依賴類(dependent class)的默認依賴(dependency),要么有一些機制確保應用行為正確。
關于IoC Containers
構造函數注入是實現依賴注入最廣泛使用的方法,如果需要傳遞不同的依賴(dependencies)在每個方法調用里,我們使用方法注入。屬性注入使用的頻率少一些。
如果我們只有一級的依賴(dependency),上面的方法可以工作的很好。但是如果具體類是其他抽象的依賴又如何。如果我們嵌套或鏈接依賴(dependencies),那實現依賴注入會有點復雜。這時可以使用IoC containers。IoC containers 可以容易的映射依賴(dependencies)當我們嵌套或鏈接依賴(dependencies)。
?
轉載于:https://www.cnblogs.com/muzinian/p/3357741.html
總結
以上是生活随笔為你收集整理的【简译】关于依赖反转原则、控制反转和依赖注入的抽象的初学者指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: poj 1106 Transmitter
- 下一篇: 心得3