函數設計準則之批次處理策略
唉唉,我差點被 Tokimeki 搞糊塗了。在他的《函數設計準則 (2)》一文中,他設計了兩個函數,一個是一次只處理一筆的 (載入單一模組、寫入單一記錄, etc) ,另一個是批次處理的。
我先從一般設計經驗來談。當我用單一處理的函數時,只會有兩個可能結果: 成功或失敗。當我用批次處理的函數時,則可能會有一個以上的錯誤狀態。然而 Tokimeki 在批次處理函數中搞了一個複雜的動作,令批次處理函數的結果可能是一個錯誤狀態 (引數 $中斷 為 TRUE)、也可能是一組錯誤狀態(陣列) (引數 $中斷 為 FALSE)。我一點都不覺得這樣做有什麼好處。
如果我用 Tokimeki 的作法,加上參數 $中斷 的設計,並選擇保用可能隨時改變 $中斷 之值的彈性,則批次處理函數的結果會有一個錯誤狀態和一組錯誤狀態兩種情形。面對這情形,我調用時的程式如下列:
如果我不用 Tokimeki 的作法,把 $中斷 拿掉,令批次處理函數的結果只會是一組錯誤狀態。則我調用時的程式如下列:
另一方面,批次處理函數的設計策略通常採 transaction 策略:除非全部動作都成功,否則復原全部狀態。以 Tokimeki 的例子來看,如果我的模組集中有十個模組要批次載入,在 transaction 策略下,只要有一個模組載入錯誤,則批次載入函數在回傳此一錯誤狀態前,必須先釋放已經載入成功的九個模組,否則會帶來副作用 (See also: 函數設計準則)。此一策略不因是否有 $中斷 參數之設計而改變。難道 $中斷 為 TRUE 時才復原狀態,而 $中斷 為 FALSE 時就不用復原狀態嗎?
當然我們也視情況決定不採用 transaction 策略,但必須有相對應的處理函數。以 Tokimeki 的例子來看,則必須要有與模組載入函數相對應的模組卸載函數。在此策略下,批次載入模組不用為了一個載入錯誤而釋放已經載入成功的九個模組,而由程式主體決定。程式主體可以選擇將已載入的模組從 $模組集 中移除後,再次調用批次載入函數;或是決定中止程序而將失敗的模組從 $模組集 中移除,再調用卸載函數釋放已經載入的模組。如下所示:
採 transaction 策略時,如上例的卸載動作須設計在載入函數之中;不採 trasaction 策略時,則要獨立設計為相對應的反批次處理函數。而 Tokimeki 的想法,似乎想將這兩種策略設計在一起,而以引數 $中斷 決定批次處理函數內部要採哪種策略。這種想法不是不行,但以我個人經驗而言,這種想法在實踐時很容易失控,複雜度會不斷增加。
樂多舊回應