CSS

Showing posts with label Design Patterns. Show all posts
Showing posts with label Design Patterns. Show all posts

[Timeless way of Building] Pattern and Code Convention

Code Convention 是一連串Pattern的集合

Pattern是從許許多多解決問題的方法中,所找出來的,具有共通性的解法
Pattern是從context中誕生出來的,並由人命名而成為design pattern

Timeless way of building中,提到了好的建築中會有的Pattern
包括窗戶的大小,坐北朝南,房間的位址等等.
這些Pattern並沒有名稱,但是卻讓建築更為美好而偉大
我們稱為Quality Without A Name
而好的code也必須要有這些QWAN

1.一致性 : 大雜院 vs 社區

在code project中,我們時常會看到多種不同的寫法,
難以理解的命名與無法找到檔案的目錄架構
有統一的Convention可以讓檔案結構更容易理解
ex : 美國街道的street vs avenue,street為東西向,avenue為南北向,讓人一看就知道這條路往哪個方向
ex : 傳統的專案架構: doc,bin,obj,src...
ex : Pascal , _Camel等命名方式

2.更好的寫法Pattern與設計的指導原則

ex:使用多個if()來取代巢狀的if-else
ex:制定exception handle的規則
ex:遇到不知道的情況以簡單的方法優先
ex:使用String.isNullOrEmpty()來檢查空字串

3.讓犯錯變得困難
制定checkin policy : 於SourceControl設定code的檢查原則,要違反必須要設定attribute
使用using的關鍵字來管理連線,避免浪費連線資源
(Javascript)使用(function())()這種closure pattern來保持namespace的清潔

[Hollywood Principle] Dependency Injection Pattern

今天讀了Agile上面有關DIP的段落,乘機把它拿出來再複習一下:

首先,為什麼要用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的詳細說明