前言php
關於這個話題, 網上有不少文章,這裏, 我但願經過最簡單的話語與你們分享.
依賴注入和控制反轉兩個概念讓不少初學這迷惑, 以爲玄之又玄,高深莫測.
這裏想先說明兩點:java
這裏經過一個簡單的案例來講明.
在公司裏有一個常見的案例: "把任務指派個程序員完成".android
把這個案例用面向對象(OO)的方式來設計,一般在面向對象設計中,名詞皆可設計爲對象
這句話裏"任務","程序員"是名詞,因此咱們考慮建立兩個Class: Task 和 Phper (php 程序員)程序員
文件: Phper.java面試
package demo; public class Phper { private String name; public Phper(String name){ this.name=name; } public void writeCode(){ System.out.println(this.name + " is writing php code"); } }
文件: Task.java編程
package demo; public class Task { private String name; private Phper owner; public Task(String name){ this.name =name; this.owner = new Phper("zhang3"); } public void start(){ System.out.println(this.name+ " started"); this.owner.writeCode(); } }
文件: MyFramework.java, 這是個簡單的測試程序.數組
package demo; public class MyFramework { public static void main(String[] args) { Task t = new Task("Task #1"); t.start(); } }
運行結果:
Task #1 started
hang3 is writing php codeapp
咱們看一看這個設計有什麼問題?
若是隻是爲了完成某個臨時的任務,程序即寫即仍,這沒有問題,只要完成任務便可.
可是若是同事仰慕你的設計,要重用你的代碼.你把程序打成一個類庫(jar包)發給同事.
如今問題來了,同事發現這個Task 類 和 程序員 zhang3 綁定在一塊兒,他全部建立的Task,都是程序員zhang3負責,他要把一些任務指派給Lee4, 就須要修改Task的源程序, 若是沒有Task的源程序,就沒法把任務指派給他人. 而一般類庫(jar包)的使用者一般不須要也不該該來修改類庫的源碼,若是你們都來修改類庫的源碼,類庫就失去了重用的設計初衷.框架
咱們很天然的想到,應該讓用戶來指派任務負責人. 因而有了新的設計.ide
文件: Phper.java 不變.
文件: Task.java
package demo; public class Task { private String name; private Phper owner; public Task(String name){ this.name =name; } public void setOwner(Phper owner){ this.owner = owner; } public void start(){ System.out.println(this.name+ " started"); this.owner.writeCode(); } }
文件: MyFramework.java, 這是個簡單的測試程序.
package demo; public class MyFramework { public static void main(String[] args) { Task t = new Task("Task #1"); Phper owner = new Phper("lee4"); t.setOwner(owner); t.start(); } }
這樣用戶就可在使用時指派特定的PHP程序員.
咱們知道,任務依賴程序員,Task類依賴Phper類,以前,Task類綁定特定的實例,如今這種依賴能夠在使用時按需綁定,這就是依賴注入(DI).
這個例子,咱們經過方法setOwner注入依賴對象,
另一個常見的注入辦法是在Task的構造函數注入:
public Task(String name,Phper owner){ this.name = name; this.owner = owner; }
在Java開發中,把一個對象實例傳給一個新建對象的狀況十分廣泛,一般這就是注入依賴.
Step2 的設計實現了依賴注入.
咱們來看看Step2 的設計有什麼問題.
若是公司是一個單純使用PHP的公司,全部開發任務都有Phper 來完成,這樣這個設就已經很好了,不用優化.
可是隨着公司的發展,有些任務須要JAVA來完成,公司招了寫Javaer (java程序員),如今問題來了,這個Task類庫的的使用者發現,任務只能指派給Phper,
一個很天然的需求就是Task應該便可指派給Phper也可指派給Javaer.
咱們發現無論Phper 仍是 Javaer 都是Coder(程序員), 把Task類對Phper類的依賴改成對Coder 的依賴便可.
這個Coder能夠設計爲父類或接口,Phper 或 Javaer 經過繼承父類或實現接口 達到歸爲一類的目的.
選擇父類仍是接口,主要看Coder裏是否有不少共用的邏輯代碼,若是是,就選擇父類
不然就選接口.
這裏咱們選擇接口的辦法:
package demo; public interface Coder { public void writeCode(); }
package demo; public class Phper implements Coder { private String name; public Phper(String name){ this.name=name; } public void writeCode(){ System.out.println(this.name + " is writing php code"); } }
package demo; public class Javaer implements Coder { private String name; public Javaer(String name){ this.name=name; } public void writeCode(){ System.out.println(this.name + " is writing java code"); } }
package demo; public class Task { private String name; private Coder owner; public Task(String name){ this.name =name; } public void setOwner(Coder owner){ this.owner = owner; } public void start(){ System.out.println(this.name+ " started"); this.owner.writeCode(); } }
package demo; public class MyFramework { public static void main(String[] args) { Task t = new Task("Task #1"); // Phper, Javaer 都是Coder,能夠賦值 Coder owner = new Phper("lee4"); //Coder owner = new Javaer("Wang5"); t.setOwner(owner); t.start(); } }
如今用戶能夠和方便的把任務指派給Javaer 了,若是有新的Pythoner加入,沒問題.
類庫的使用者只需讓Pythoner實現(implements)了Coder接口,就可把任務指派給Pythoner, 無需修改Task 源碼, 提升了類庫的可擴展性.
回顧一下,咱們開發的Task類,
在Step1 中與Task與特定實例綁定(zhang3 Phper)
在Step2 中與Task與特定類型綁定(Phper)
在Step3 中與Task與特定接口綁定(Coder)
雖然都是綁定, 從Step1,Step2 到 Step3 靈活性可擴展性是依次提升的.
Step1 做爲反面教材不可取, 至因而否須要從Step2 提高爲Step3, 要看具體狀況.
若是依賴的類型是惟一的Step2 就能夠, 若是選項不少就選Step3設計.
依賴注入(DI)實現了控制反轉(IoC)的思想.
看看怎麼反轉的?
Step1 程序
this.owner = new Phper("zhang3");
Step1 設計中 任務Task 依賴負責人owner, 就主動新建一個Phper 賦值給owner,
這裏是新建,也多是在容器中獲取一個現成的Phper,新建仍是獲取,可有可無,關鍵是賦值, 主動賦值. 這裏提一個賦值權的概念.
在Step2 和 Step3, Task 的 owner 是被動賦值的.誰來賦值,Task本身不關心,多是類庫的用戶,也多是框架或容器.
Task交出賦值權, 從主動賦值到被動賦值, 這就是控制反轉.
什麼是控制反轉 ?
簡單的說從主動變被動就是控制反轉.
上文以依賴注入的例子,對控制反轉作了個簡單的解釋.
控制反轉是一個很普遍的概念, 依賴注入是控制反轉的一個例子,但控制反轉的例子還不少,甚至與軟件開發無關.
這有點相似二八定律,人們老是用具體的實例解釋二八定律,具體的實例不等與二八定律(不瞭解二八定律的朋友,請輕鬆忽略這個類比)
如今從其餘方面談一談控制反轉.
傳統的程序開發,人們老是從main 函數開始,調用各類各樣的庫來完成一個程序.
這樣的開發,開發者控制着整個運行過程.
而如今人們使用框架(Framework)開發,使用框架時,框架控制着整個運行過程.
對比如下的兩個簡單程序:
package demo; public class Activity { public Activity(){ this.onCreate(); } public void onCreate(){ System.out.println("onCreate called"); } public void sayHi(){ System.out.println("Hello world!"); } public static void main(String[] args) { Activity a = new Activity(); a.sayHi(); } }
package demo; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); tv.append("Hello "); tv.append("world!"); setContentView(tv); } }
這兩個程序最大的區別就是,前者程序的運行徹底由開發控制,後者程序的運行由Android框架控制.
兩個程序都有個onCreate方法.
前者程序中,若是開發者以爲onCreate 名稱不合適,想改成Init,沒問題,直接就能夠改, 相比下,後者的onCreate 名稱就不能修改.
由於,後者使用了框架,享受框架帶來福利的同時,就要遵循框架的規則.
這就是控制反轉.
能夠說, 控制反轉是全部框架最基本的特徵.
也是框架和普通類庫最大的不一樣點.
不少Android開發工程師在享用控制反轉帶來的便利,去不知什麼是控制反轉.
就有點像深海里的魚不知到什麼是海水同樣.
經過框架能夠把許多共用的邏輯放到框架裏,讓用戶專一本身程序的邏輯.
這也是爲何如今,不管手機開發,網頁開發,仍是桌面程序, 也不論是Java,PHP,仍是Python框架無處不在.
回顧下以前的文件: MyFramework.java
package demo; public class MyFramework { public static void main(String[] args) { Task t = new Task("Task #1"); Coder owner = new Phper("lee4"); t.setOwner(owner); t.start(); } }
這只是簡單的測試程序,取名爲MyFramework, 是由於它擁有框架3個最基本特徵
這裏建立了兩個對象,實際框架可能會建立數千個對象,可能經過工廠類而不是直接建立,
這裏直接裝配對象,實際框架可能用XML 文件描述要建立的對象和裝配邏輯.
固然實際的框架還有不少這裏沒涉及的內容,只是但願經過這個簡單的例子,你們對框架有個初步認識.
控制反轉還有一個漂亮的比喻:
好萊塢原則(Hollywood principle)
"不要打電話給咱們,咱們會打給你(若是合適)" ("don't call us, we'll call you." )
這是好萊塢電影公司對面試者常見的答覆.
事實上,不僅電影行業,基本上全部公司人力資源部對面試者都這樣說.
讓面試者從主動聯繫轉換爲被動等待.
爲了增長本文的趣味性,這裏在舉個比喻講述控制反轉.
人們談戀愛,在之前一般是男追女,如今時代進步了,女追男也很常見.
這也是控制反轉
體會下你追女孩和女孩追你的區別:
你追女孩時,你是主動的,你是標準制定者, 要求身高多少,顏值多少,知足你的標準,你纔去追,追誰,何時追, 你說了算.
這就相似,框架制定接口規範,對實現了接口的類調用.
等女孩追你時,你是被動的,她是標準制定者,要求有車,有房等,你買車,買房,努力工做掙錢,是爲了達到標準(既實現接口規範), 你萬事具有, 處於候追狀態, 但時誰來追你,何時追,你不知道.
這就是主動和被動的區別,也是爲何男的偏好主動的緣由.
這裏模仿好萊塢原則,提一箇中國帥哥原則:"不要追哥, 哥來追你(若是合適)",
簡稱CGP.( Chinese gentleman principle: "don't court me, I will court you")
面向對象的設計思想
第一節 提到在面向對象設計中,名詞皆對象,這裏作些補充.
當面對一個項目,作系統設計時,第一個問題就是,系統裏要設計哪些類?
最簡單的辦法就是,把要設計系統的名詞提出來,一般,名詞可設計爲對象,
可是否全部名詞都須要設計對應的類呢? 要具體問題具體分析.不是不能夠,是否有必要.
有時候須要把一些動詞名詞化, 看看現實生活中, 寫做是動詞,全部寫做的人叫什麼? 沒有合適的稱呼,咱們就叫做者, 閱讀是動詞,閱讀的人就稱讀者. 中文經過加"者","手"使動詞名詞化,舞者,歌手,投手,射手皆是這類.
英語世界也相似,經過er, or等後綴使動詞名詞化, 如singer,writer,reader,actor, visitor.
現實生活這樣, Java世界也同樣.
Java經過able,or後綴使動詞名詞化.如Runnable,Serializable,Parcelable Comparator,Iterator.
Runnable便可以運行的東西(類) ,其餘相似.
瞭解了動詞名詞化,對java裏的不少類就容易理解了.
相關術語(行話)解釋
Java 裏術語滿天飛, 讓初學者望而生畏. 若是你不想讓不少術語影響學習,這一節可忽視.
瞭解了原理,叫什麼並不重要. 瞭解些術語的好處是便於溝通和閱讀外文資料,還有就是讓人看起來很專業的樣子.
Coder owner = new Phper("lee4");
Coder owner = new Phper("lee4");
Phper p = (Phper) owner;
但願上述內容, 對你們有所幫助, 謝謝.
快才助手, 在電腦上操做手機, Android屏幕同步軟件
本文做者手工打造,熱情推薦,網址: http://www.kwaicai.com