最近更新: 2007-02-14

函數設計準則之批次處理策略

唉唉,我差點被 Tokimeki 搞糊塗了。在他的《函數設計準則 (2)》一文中,他設計了兩個函數,一個是一次只處理一筆的 (載入單一模組、寫入單一記錄, etc) ,另一個是批次處理的。

我先從一般設計經驗來談。當我用單一處理的函數時,只會有兩個可能結果: 成功或失敗。當我用批次處理的函數時,則可能會有一個以上的錯誤狀態。然而 Tokimeki 在批次處理函數中搞了一個複雜的動作,令批次處理函數的結果可能是一個錯誤狀態 (引數 $中斷TRUE)、也可能是一組錯誤狀態(陣列) (引數 $中斷FALSE)。我一點都不覺得這樣做有什麼好處。

如果我用 Tokimeki 的作法,加上參數 $中斷 的設計,並選擇保用可能隨時改變 $中斷 之值的彈性,則批次處理函數的結果會有一個錯誤狀態和一組錯誤狀態兩種情形。面對這情形,我調用時的程式如下列:

$results = 載入:模組集($模組集, $中斷);
if (is_string($results)) {
    echo $ersults;
}
else if (is_array($results)) {
    foreach ($results as $errorStatus) {
        echo $errorStatus;
    }
}
else {
    echo 'OK';
}

如果我不用 Tokimeki 的作法,把 $中斷 拿掉,令批次處理函數的結果只會是一組錯誤狀態。則我調用時的程式如下列:

$results = 載入:模組集($模組集);
if ($results !== TRUE) {
    foreach ($results as $errorStatus) {
        echo $errorStatus;
    }
}
else {
    echo 'OK';
}

另一方面,批次處理函數的設計策略通常採 transaction 策略:除非全部動作都成功,否則復原全部狀態。以 Tokimeki 的例子來看,如果我的模組集中有十個模組要批次載入,在 transaction 策略下,只要有一個模組載入錯誤,則批次載入函數在回傳此一錯誤狀態前,必須先釋放已經載入成功的九個模組,否則會帶來副作用 (See also: 函數設計準則)。此一策略不因是否有 $中斷 參數之設計而改變。難道 $中斷TRUE 時才復原狀態,而 $中斷FALSE 時就不用復原狀態嗎?

當然我們也視情況決定不採用 transaction 策略,但必須有相對應的處理函數。以 Tokimeki 的例子來看,則必須要有與模組載入函數相對應的模組卸載函數。在此策略下,批次載入模組不用為了一個載入錯誤而釋放已經載入成功的九個模組,而由程式主體決定。程式主體可以選擇將已載入的模組從 $模組集 中移除後,再次調用批次載入函數;或是決定中止程序而將失敗的模組從 $模組集 中移除,再調用卸載函數釋放已經載入的模組。如下所示:

while (($results = 載入:模組集($模組集)) !== TRUE) {
    if ($再試幾次) {
        $模組集 = array();
        foreach ($results as $result) {
            $模組集[] = $result['模組'];
        }
        --$再試幾次;
    }
    else {
        $釋放模組集 = ...
        卸載:模組集($釋放模組集);
        break;
    }
}

採 transaction 策略時,如上例的卸載動作須設計在載入函數之中;不採 trasaction 策略時,則要獨立設計為相對應的反批次處理函數。而 Tokimeki 的想法,似乎想將這兩種策略設計在一起,而以引數 $中斷 決定批次處理函數內部要採哪種策略。這種想法不是不行,但以我個人經驗而言,這種想法在實踐時很容易失控,複雜度會不斷增加。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/2722505.html

樂多舊回應
HACGIS@gmail.com(tokimeki) (#comment-3967451)
Wed, 14 Feb 2007 13:44:24 +0800
阿,我用詞不精準造成你的困擾了~
說實話,transcation策略我還沒深思,不過就我提的那幾個例子,實在不適合用transcation策略來作,原因出在PHP中沒辦法卸載已經載入的extension。

那篇批次處理的文章,將來我會去修訂,中斷是不用說了,我確定會有實質上的意義,但我目前在思考的是,執行失敗後傳回失敗的資訊是否有意義。(失敗與錯誤是兩回事,所以我不以錯誤處理的方向去思考)

transcation策略是一種設計上的選擇,他的基本精神是要把函數的邊際效應降到最低,可我個人卻從另一個角度看這件事:是否可以善用邊際效應,達到整體設計的優化?
比如說像ROR的精神是用慣例取代設定,慣例是否也可說是一種邊際效應?

當然,這兩者的粒子大小不同,或許不可一概而論,但是我個人是覺得,如果整體的設計準則能夠一致,應該不至於讓複雜度增加。
未留名 (#comment-3967729)
Wed, 14 Feb 2007 14:19:34 +0800
你所思考的事,我幾乎可以 case by case 的說出好幾種處理策略。這之中確實有一個準則,但這個準則是基於實務經驗而非邏輯的。很難事先預想推算。

另一方面,你的想法有點像在問:有沒有 Design Pattern...

我這人很少看那些 Design Pattern 的書,其實那些 Design Pattern 也是碰了一堆壁之經驗摸索出來的處理策略。如果你碰過的壁夠硬、幹過的蠢事夠多 (抱歉,我是在說我自己 XD) ,自然就會有 Design Pattern 成形了;當然啦,看那些 Design Pattern 的書,可以讓你少挨些痛苦就是了。
HACGIS@gmail.com(tokimeki) (#comment-3968587)
Wed, 14 Feb 2007 15:48:09 +0800
是的,我的確在問有沒有DP。
不過我問的不是G4他們那個層次提出的DP,而是一些教科書上沒講的實務經驗,也就是說,我們「應該這樣」設計的準則。

我嘗試找出正確的詞彙、應用的情境等等來作論述,希望能有一些確切的準則呈現出來。
未留名 (#comment-3972845)
Wed, 14 Feb 2007 22:45:18 +0800
嗯,問一下。G4 是誰?他們提的是什麼?

你的立意不錯。不過... 恕我直言,我認為在累積足夠的嘗試與失敗設計經驗之前,似乎不應過早嘗試 Design Pattern 這種事物。所以我不談 Design Pattern 。
HACGIS@gmail.com(tokimeki) (#comment-3974401)
Thu, 15 Feb 2007 00:46:10 +0800
G4就是提出Design Pattern的四巨頭。
我也不談DP,我談的是顆粒比較細的函數設計準則,這部份可以藉由討論實務的方式一點一滴的累積。