2020年10月25日

PHP | 提升PHP程式品質,不要濫用陣列(Array)

PHP的有非常高度的隨便性,我是說自由性。

這樣的自由性,對於新手來說非常容易入手,因為怎麼寫都會動,但相對地非常容易寫出可怕的東西,導致不少PHP專案,最終都會變成可怕的樣子,難以維護。

其中有一個罪魁禍首,就是array。

php的array真的是非常方便,愛怎麼用就怎麼用,key可以是數字可以是字串,還可以任意動態增加新的key值,或是任意增加array的維數。

但是,也是這樣的方便性,當array在function間傳來傳去,常常傳到最後,根本不知道array裡面,或是array裡面該有什麼。

所以我認為,不要過於濫用array。

不要濫用 Array

舉一個取得使用者資訊的小例子:

$data = getUserData(['id' => 123]);

function getUserData($data) 
{
    $data['name'] = getName($data);    
    $data['store'] = getStore($data);
    $data['storeNameForDisplay'] = getStoreNameForDisplay($data);

    return $data;
}

function getName($data) 
{
    $name = getNameById($data['id']);
    return $name;
}

function getStore($data) 
{
    $storeInfo = getStoreInfoByUserId($data['id']);
    return [
        'id' = $storeInfo['id'],
        'name' = $storeInfo['name'],
    ];
}

function getStoreNameForDisplay($data)
{
    return $data['name'].' '.$data['store']['name'];
}

如上述的例子,getUserData(),你是無法從它的function定義看出要丟什麼東西進去的,只能一行一行看。

再來,即便知道要丟array,你也不知道要丟什麼key進去,直到你把getUserData()裡面每一行所呼叫的function都看過了,你才知道原來$data需要id這個key(在getName()getStore()中都有用到)。

而其中store的這個key的value,是一個array,透過getStore()回傳的,在下一個function getStoreNameForDisplay()中,會用到store array的name,這也是從getStoreNameForDisplay的介面看不出來的。

若今天有人任意把getStore的回傳格式改了,他可能根本也不會想到,這居然會影響到getStoreNameForDisplay這隻function吧?(莫名的耦合性)

建議的重構方向:

  1. 減少function間用array的形式傳遞參數
  2. 如果真的要用array,在進到其他function之前,盡量要拆解掉

重構結果:

$data = getUserData(123);

function getUserData($userId) 
{
    $userName = getUserName($userId);    
    $store = getStoreByUserId($userId);
    $storeNameForDisplay = getStoreNameForDisplay($userName, $store['name']);

    $userData = [
        'name' => $userName,
        'store' => $store,
        'storeNameForDisplay' => $storeNameForDisplay,
    ];

    return $userData;
}

function getUserName($userId) 
{
    $userName = getUserNameByUserId($userId);
    return $userName;
}

function getStoreByUserId($userId) 
{
    $storeInfo = getStoreInfoByUserId($userId);
    return [
        'id' = $storeInfo['id'],
        'name' = $storeInfo['name'],
    ];
}

function getStoreNameForDisplay($userName, $storeName)
{
    return $userName.' '.$storeName;
}

明確定義getUserData($userId)接受user id作為參數,而不是傳一個不知所以的$data進來。

在呼叫getStoreNameForDisplay的時候,不直接把$srore傳進去,而是取出$store的name,再傳入getStoreNameForDisplay中。

雖然這樣看起來比一開始的做法還要冗長,但每個function的介面跟回傳值,都變得更加清楚了,對於日後看程式或維護,會很有幫助。

(不過上述重構後的程式還有一些可以改善的空間就是了。)

結論

今天一個簡單的小專案,那當然快速方便為主。但是工程師不可能永遠只做小專案,系統也不可能永遠都是遞交出去就沒事了,總是會有一些後續的維護或擴充的需求。

如果到時候,看到的是,一堆莫名其妙的function,一堆array飛來飛去,裡面的key根本不知道是什麼,想必是相當頭痛的。

回歸正題,PHP array固然方便好用,但濫用的話,會造成可維護性降低,我們寧可一開始多花一點力氣,把程式寫的清楚一點,所謂長痛不如短痛,一開始的辛苦,可以降低後面的痛苦。

而我的作法,用一句話做總結就是:捨棄部分的彈性,降低不確定性