首先,為什麼要用DIP呢?
書上舉了一個很好玩的例子來說明,叫"Hollywood Principle"
意思是"Don't Call me, I'll Call you"
好萊塢的經紀人不希望面試者主動聯絡他們,而是當他們決定要用你的時候才會主動聯絡。
以程式來說,就是讓物件自己決定要如何呼叫其他物件,而不是依賴於其他物件。
假設說今天我們要設計一個可以讀取Book資料的Reader
class Reader{
private Book book = new Book("Harry Potter");
public void read(int page){
book.read(page);
}
}
Reader依賴於book這個class的函式,
所以當我們要為這個程式增加可以閱讀EBook功能的時候,我們必須加入邏輯判斷來區別
第一個想到的就是新增TypeCode判斷,
於是醜陋的程式碼就產生了...
class Reader{
static final int BOOK = 1,EBOOK = 2;
public void read(int page, int bookType){
if(bookType = BOOK){
Book book = new Book("Harry Potter");
book.read(page);
}else if(bookType = EBOOK){
EBook book = new EBook("Harry Potter");
book.read(page);
}
}
}
使用Dependency Injection Pattern,能使Reader這個Class不會被book這個class綁死,
相反的,Reader可以先宣告自己會使用的介面,等於是告訴其他Class
"我會用這個方式來呼叫你,如果你想讓我用的話,就先實做這個介面":
class Reader{
private IBook book;
public void read(int page){
book.read(page);
}
public Reader(IBook book){
this.book = book;
}
}
class Book implement IBook{...}
class EBook implement IBook{...}
使用介面,或是abstract class來宣告使用的介面,
讓Reader這個class可以不被單一的Book class所綁死
只要是實作IBook這個介面的Class,Reader都可以使用
public Reader(IBook book){
this.book = book;
}
而這段程式碼在呼叫Reader的建構式時傳入它要使用的Book
這就是Dependency Injection(控制反轉)的一種
是當你要使用物件的時候才傳入物件之間的依賴關係,而不是在一開始就在程式碼中指定
這樣可以保持物件之間彼此的鬆散偶合,增加擴充性.
另外,在作單元測試時,DI也是很有用的一個Pattern
如果我們要測試Reader的Read()這個函式,但是Read這個函式又會呼叫到Book,
然後Book會連接到背後的資料物件...這樣我們無法知道說測試的成功或失敗,
是因為Reader的關係,還是Book,或是背後的資料庫的問題.
這個時候我們就可以使用Dependency Injection的特性,將Book與Reader之間的關係給切開,
先寫一個測試用的Book,實作IBook的介面,並且紀錄是否被呼叫:
public class TestBook implement IBook(){
public bool readed = false;
public void read(int page){
readed = true;
}
}
//測試案例:
public void ReaderTest(){
TestBook book = new TestBook()
Reader reader = new Reader(book);
reader.read();
assertTrue(book.readed);
}
利用這個方式,我們可以只測試我們想測試的邏輯.
也可以寫一個假的Data Access Class,將資料庫的部分先抽換掉,
來測試系統的介面與流程是否ok.
more:
Martin Flower提出的DI概念
良葛格對DI的詳細說明
No comments:
Post a Comment