標籤:
在介紹原廠模式與控制反轉(Inversion of Control)及依賴注入(Dependency Injection)之前,先介紹下類的調用方法。目前調用方法總共有3種:1.自己建立;2.原廠模式;3.外部注入,其中外部注入即為控制反轉/依賴注入模式(IoC/DI)。我們可以用3個形象的東西來分別表示它們,就是new、get、set。顧名思義,new表示自己建立,get表示主動去取(即工廠),set表示是被別人送進來的(即注入),其中get和set分別表示了主動去取和等待送來兩種截然相反的特性,這3個單詞代表了3種方法的思想精髓。
無論是那一種方法,都存在兩個角色,那就是調用者和被調用者。下面我們通過執行個體來講解這3種方法的具體含義。首先,我們設定調用對象為學生對象Student,被調用者對象為圖書對象Book,要設計的代碼功能是學生學習圖書。我們一般習慣於一種思維編程方式:介面驅動,可以提供不同靈活的子類實現:
//Book介面public interface IBook{ public void learn();}//BookA實作類別public class BookA implements IBook { public void learn(){ System.out.println("學習BookA"); }}//BookB實作類別public class BookB implements IBook { public void learn(){ System.out.println("學習BookB"); }}
下面來看看這3中方法是如何調用圖書類。
1)new---自己建立
public class Student { public void learnBookA(){ IBook book = new BookA(); book.learn(); } public void learnBookB(){ IBook book = new BookB(); book.learn(); } }
該方法在調用者Student需要調用被調用者IBook時,需要自己來建立一個IBook對象。這種做法的缺點是無法更換被調用者,並且要負責被調用者的整個生命週期。
2)get---原廠模式
一切對象都有自己建立的缺點是建立的對象會到處分散,造成管理上的麻煩,如要更換,則需要大量修改代碼。原廠模式就可以來解決這個問題。
//圖書工廠public class BookFactory{ public static IBook getBookA() { IBook book = new BookA(); return book; } public static IBook getBookB() { IBook book = new BookB(); return book; }}//學生類public class Student { public void learnBookA(){ IBook book = BookFactory.getBookA(); book.learn(); } public void learnBookB(){ IBook book = BookFactory.getBookB(); book.learn(); }}
此時多了一個工廠類,將對象建立提取到工廠類中,調用者無需考慮對象的建立,只管從工廠中拿,在修改被調用者是也無需改動太多的代碼。但是,對象的建立依然不靈活,以為兌現的取得完全取決於工廠,有多了一道中間的工序。
3)set---外部注入
顯然,第一種方式依賴於被調用者對象,第二種方式依賴於工廠,都存在依賴性。為了徹底解決依賴性的問題,採取了外部注入的方式。外部注入就要用到上一章的動態編程的反射機制。利用這種方式構建IoC容器。對於IoC容器,可以把它看做是原廠模式的升華,它好比一個大工廠,下面就來說說注入的三種類型
1.介面注入
public class Student{ private IBook bookA; public init() { bookA = (IBook)Class.forName("BookA").newInstance(); }}
通過動態建立BookA的執行個體來注入,這種方式仍然依賴於IBook的實現。
2.構造注入
依賴關係是通過類的建構函式建立的,容器通過調用類的構造方法,將其所需的依賴關係注入其中。
public class Student { private BookA bookA; public Student (BookA booA){this.bookA = bookA; } public void learn() {bookA.learn(); }}public class Factory { public static getInstance(){Class clazz = BookA.class; //建立類BookA的Class對象Class[] param = new Class[]{BookA.class};try{ Constructor con = clazz.getConstructor(param); BookA bookA = (BookA)con.newInstance(str); Student student1 = new Student(bookA);}catch (Exception e){ e.printStackTrace();}return student1; }}
這樣就通過Factory.getInstance()直接擷取又有屬性bookA的一個Student執行個體。
3.設定注入
受控對象通過屬性來表達自己所以來的對象和所需配置的值。只要為對象添加setter方法即可。
public class Student { private BookA bookA; public BookA getBookA(BookA booA){return bookA; } public void setBookA(BookA booA){this.bookA = bookA; } public void learn() {bookA.learn(); }}public class Factory { public static getInstance(String fieldName){Class clazz = BookA.class; //建立類BookA的Class對象Class student = Student.class;Class[] param = new Class[]{BookA.class};try{ Method method = student.getMethod("set"+change(fieldName),param); BookA bookA = (BookA)clazz.newInstance(); BookA[] book = new BookA[] {bookA}; Object obj = student.newInstance(); method.invoke(obj,book); Student student1 = (Student) obj;}catch (Exception e){ e.printStackTrace();}return student1; } public static String change(String str) {String subString = str.substring(0,1);subString = subString.toUpperCase();str = str.substring(1,str.length());str = subString + str;return str; }}
這樣只需調用Factory.getInstance("bookA")即可獲得Student的執行個體。 依賴注入的優點在於在容器中定義對象,容器會自動組建出一個執行個體,缺點就是產生一個對象的步驟變得複雜了,對於不習慣這種方式的人會覺得有些彆扭和不直觀。 總結:從原廠模式到依賴注入,搞得那麼複雜究竟為了什麼呢?原廠模式的作用就是將對象的建立擊中到工廠容器中,這樣對於對象的修改也集中到容器中進行,便於維護。而原廠模式由於不通用,故對於不同的對象就需要在容器中建立不同的方法,因此利用基於反射機制的動態編程技術又可以解決此問題,但是產生對象的過程越來越繁瑣。
引文連結:
原廠模式、控制反轉及依賴注入
c#實現依賴注入
原廠模式、控制反轉及依賴注入