l

2012年5月21日 星期一

Implementation Patterns: Composed Method

May 20 22:23~22:42

2012-05-16 08.59.11


今天沒什麼廢話,直接進入主題。今天要繼續介紹Kent Beck所寫的Implementation Patterns這本書的第77-78頁所介紹的Composed Method這個implementation pattern。

有寫過程式的鄉民們應該都知道,在寫程式的時候,每一個函數(function)或是方法(method)的內容寫法,不外乎有以下三大類:

  • 全部的程式邏輯都直接寫在method body裡面,例如下面這個add method的邏輯,:

int add(int x, int y){

     return x + y;

}

  • method的實作式全部都是藉由呼叫其他的methods來達成,例如:

void doSomething() {

      readInput();

      process();

      output();

}

  • method的實作包含部分直接寫在method body裡面的邏輯,以及部分呼叫其他的methods來達成,例如:

void addChild(Object childNode) {

       if (atCapacity())
                grow();


        addElement(childNode);

}

依據Kent Beck的說法,Composed Method的有兩個要求:

  • 藉由呼叫其他method來完成自己的工作。
  • 在Composed Method內部的每一個操作(operation)與被Composed Method所呼叫的每一個method必須要有差不多大小的抽象程度。

從上面這兩點來看,add()顯然不是一個Composed Method,而doSomething()和addChile()則屬於Composed Method。

So what?看到這邊鄉民們可能會這樣想,這有什麼了不起的,程式不就是這樣寫的,把實作寫在自己身上,或是呼叫其他人,或是兩者混和。既然這本書的目的是希望能寫出容易理解的程式,當然Kent Beck提出Composed Method這個pattern一定有一些學問在裡面。看一下書本中的例子:

void compute() {

input();

flags |= 0x0080;

output();

}

不曉得鄉民們有沒有寫過或是看過類似的程式?Teddy倒是經常看到。上面這個compute()的寫法,看起來像是一個Composed Method,但是這是一個寫得不好的Composed Method。為什麼?因為違反了Composed Method的第二條規則:「在Composed Method內部的每一個操作(operation)與被Composed Method所呼叫的每一個method必須要有差不多大小的抽象程度」。compute()一共只有三行程式,很明顯的input()與output()的抽象程度是類似的,但是這個第二行的 flag |= 0x0080 是怎麼一回事,和其他兩行的程式邏輯抽象程度也未免相差太多了吧。這樣的Composed Method顯然不易閱讀也不易理解。

***

把一個(內容很長的)method的內部邏輯依據功能或是目的,分別抽離出來變成若干個小methods(可參考Extract Method這個refactoring),然後在原本的method中去呼叫這些被抽離出的methods,那麼原本的method就變成Composed Method。接著鄉民可能會問以下幾個問題:

  • 被抽離出來的method要有多長:這個問題其實沒有什麼正確的答案,有人說method長度列印出來之後不要超過一頁(A4或是Letter size),或是一個method不要超過5-15行(好嚴格啊)。
  • Composed Method內部呼叫太多很小的methods,這樣不會對程式效能造成影響嗎:會,當然會有影響。Kent Beck做了一個小測試,呼叫某個method一百萬次,比直接在迴圈中執行該method的內容平均大概慢20-30%。Kent Beck認為這樣的效能差距不足引影響大部分的程式。此外,幾乎所有物件導向大師都會告訴你,有一個結構清楚的程式,反倒比較容易在效能測試中找出系統的瓶頸並加以改善。
  • 有時候某個method的內容包含了很多細節,這些細節有助於了解這段程式碼,但是卻不容易切割成抽象程度大小差不多的methods。該怎麼辦:答案也很簡單,套用另外一個叫做Method Object的pattern(這個pattern改天再介紹)。

***

相信只要有寫過程式的鄉民,一定都寫過Composed Method。至於Composed Method是否容易被理解,重點就在於有沒有讓Composed Method內部的每個operation都有差不多大小的抽象程度。這是Teddy覺得讀完這個pattern最大的收穫。

***

友藏內心獨白:小東西也隱含著大學問。

1 則留言:

  1. 最近讀了「重構與模式」,看了裡面的範例後,再回來看這篇,覺得特別有感。RD 在寫程式時,「抽象程度」會跳上跳下的,這很正常,但寫完測完後,如果就這樣把程式碼留在那邊,就等於是逼讀者跟著你當初「混亂失序」的思緒走。這其實是件蠻失禮的事 XD

    回覆刪除