石頭在回應這一系列中提到調用者與被調用者的不同觀點,以及四種函數的分別。
我在寫這一系列時其實都想過了,但我著重的部份不是「可不可以」而是「應不應該」,有許多地方我還在調整,但因為部落格可以很快取得不同的迴響,所以我決定先發表再說,文章的內容日後可以再進行整理與修改。

借用石頭的調用者與被調用者的觀點,這篇文章來提一下調用者的部份,先來看個例子:

function 載入:模組集($模組集, $中斷 = true){
    $輸出 = array();
    $模組集 = &條件:陣列($模組集, false);
    foreach($模組集 as $模組 => $條件){
        if(!載入:模組($模組, $條件)){
            if($中斷){
                return $模組;
            }
            $輸出[] = $模組;
        }
    }
    if(empty($輸出)){
        return true;
    }else{
        return $輸出;
    }
}

function &條件:陣列(&$陣列, $預設 = true){
    $預設 = ($預設) ? true : false;
    if(!is_array($陣列)){
        return array($陣列 => $預設);
    }
    $輸出 = array();
    foreach($陣列 as $索引 => $項目){
        if(is_bool($項目)){
            $輸出[$索引] = $項目;
        }else{
            $輸出[$項目] = $預設;
        }
    }
    return $輸出;
}

這幾個函數我都還在想有沒有更好的設計方式,所以請不要以為這就是我的定見,只是我上次拋出一個話題,必須要很快的說明相關的設計準則與概念,所以把我思考的筆記與實例拿出來作說明。

「載入:模組集」這個函數,會呼叫「載入:模組」作批次處理。
這個函數經過特化處理,因為「載入:模組」這個函數如果不算「中斷」這個參數的話,他其實只接受一個參數,即模組的名稱。

這裡我依然將「中斷」的概念賦予在批次處理上,這時候的責任關係就很簡單:要不往上丟,要不往下推。
而批次處理時的「中斷」概念,其實比較符合「流程中斷」,因為他並不是最底層,不會牽扯到「錯誤處理」。
這類批次的函數,其特性應與單一處理的函數,有著一致性的行為特徵,我列表在下面:
 單一批次
(全部)成功傳回 true傳回 true
失敗傳回 false傳回失敗項目陣列
中斷丟出中斷訊息傳回中斷項目

當然,這是我個人的看法,我關注的部份是「失敗處理」,而非「錯誤處理」或是「例外處理」。
所以,藉由「失敗」而引發的「錯誤」或是「例外」,只是處理的一種「手段」。
有些時候我們很無奈,有心處理但卻無法處理,只好引發「錯誤」或是「例外」,但請不要以為失敗就一定會引發錯誤或是例外,這是兩回事!

我把這類函數的特性再抄一遍:
  • 這類函數進行一些單純的動作,執行結果只有成功或是失敗兩種結果。
  • 呼叫該函數時,我們對其成功或失敗的原因不感興趣,我們只希望知道其執行結果到底是成功或是失敗。
  • 在其執行失敗時,有時會需要將程式流程中斷下來,我們需要一個條件去指示是否需要進行中斷。
請特別注意第二點:我們對其成功或失敗的原因不感興趣,我們只希望知道其執行結果到底是成功或是失敗。
這是我強調的重點!「在此前提下,所以我會這樣設計」,這是我要拿出來討論的。

那麼調用這些批次函數的狀況又如何呢?這裡有個例子:

function 選擇:模組($選擇集){
    foreach($選擇集 as $選擇 => $模組集){
        if(true === 載入:模組集($模組集, true)){
            return $選擇;
        }
    }
    return false;
}


這個函數是在做一種簡單的策略選擇的判斷,這個判斷會根據一些載入模組的組合來選擇合適的策略。
在討論下去就有點偏離主題了,舉例就到這裡,有三層:選擇:模組->載入:模組集->載入:模組。

為甚麼要特別提出「中斷」的概念?我說不上來,我只知道,沒有中斷的設計,失敗的責任是會累積的。
讓我們再看兩個函數,這兩個函數是我把中斷拿掉後,產生的兩個特例:

//  函數1
function 載入:模組($模組){
    $前綴 = (PHP_SHLIB_SUFFIX == 'dll') ? 'php_' : '';
    if(!extension_loaded($模組) && !@dl($前綴.$模組.'.'.PHP_SHLIB_SUFFIX)){
        $訊息 = '無法載入 '.$模組.' 模組';
        exit($訊息);
    }else{
        return true;
    }
}

//  函數2
function 載入:模組($模組){
    $前綴 = (PHP_SHLIB_SUFFIX == 'dll') ? 'php_' : '';
    if(!extension_loaded($模組) && !@dl($前綴.$模組.'.'.PHP_SHLIB_SUFFIX)){
        return false;
    }else{
        return true;
    }
}

看到了嗎?如果是這樣的兩個函數,要如何批次處理?就會造成我之前回應的,要多作檢查去應對。
我不否認這樣的設計比較簡單,但這是完全不承擔責任或是「先斬後奏」的設計方式。


PING:
TITLE: 函數設計準則之批次處理策略
URL: http://blog.roodo.com/rocksaying/archives/2722505.html
IP: 61.63.27.61
BLOG NAME: 石頭閒語
DATE: 02/13/2007 05:11:59 PM
Tokimeki 似乎想將兩種策略設計在一起,而以 $中斷 決定批次處理函數內部要採哪種策略。以我個人經驗,這種想法在實踐時很容易失控,複雜度會不斷增加。
創作者介紹

失落的技術

HACGIS 發表在 痞客邦 PIXNET 留言(0) 人氣()