文中代碼皆為不規(guī)范的偽代碼
程序中曾經(jīng)包含子程序和函數(shù)(可能我沒有經(jīng)歷這種年代),如今,程序中僅有函數(shù)這種方式保留下來挫望。
編程語言中的函數(shù)立润,分為內(nèi)置函數(shù)和自定義函數(shù)或類方法。內(nèi)置函數(shù)可以直接調(diào)用媳板,而自定義函數(shù)是程序員編寫的桑腮。
函數(shù)是程序的極其重要的組成部分,要寫好蛉幸,需要遵守一些原則破讨。
函數(shù)要短小。函數(shù)中不應(yīng)該容得下嵌套結(jié)構(gòu)巨缘,如if...else等添忘。在if..else、while若锁、for或foreach等嵌套結(jié)構(gòu)中搁骑,代碼不應(yīng)該超過一行,這唯一一行代碼又固,是對函數(shù)的調(diào)用仲器。請看代碼A。
function test($bool){
$age = 0;
if($bool){
$age = 5;
}else{
$age = 0;
}
return ?$age;
}
$bool = true;
echo test($bool);
上面這段代碼仰冠,函數(shù)體中包含if...else乏冀,嵌套結(jié)構(gòu),根據(jù)”函數(shù)要短小“的原則洋只,上面的代碼應(yīng)該改寫為代碼B:
function testTrue(){
$age = 5;
return $age;
}
function testFalse(){
$age = 0;
return $age;
}
$bool = true;
if($bool){
echo testTrue();
}else{
echo testFalse();
}
我喜歡用代碼B風(fēng)格的代碼辆沦。它能簡化主干代碼(抽象層次高的代碼)。實際編程中识虚,我綜合了代碼A和代碼B肢扯,因為我在代碼B的基礎(chǔ)上增加了一個函數(shù)test。請看代碼C担锤。
function testTrue(){
$age = 5;
return $age;
}
function testFalse(){
$age = 0;
return $age;
}
function test($bool){
$age = 0;
if($bool){
$age=testTrue();
}else{
$age=testFalse();
}
$bool = true;
echo test($bool);
三種風(fēng)格的代碼蔚晨,孰優(yōu)孰劣?我無法分辨肛循。編程實踐中铭腕,我選用C風(fēng)格的代碼。寫短小的函數(shù)多糠,原因是累舷,短小的函數(shù),易讀夹孔,便于調(diào)試笋粟。當然怀挠,還受制于許多程序員編程實踐中總結(jié)出的編程原則:一個函數(shù)只做一件事情。
《代碼整潔之道》用這樣的話強調(diào)這個編程原則:三十多年來害捕,無數(shù)人用無數(shù)不同的表達方式傳遞出這樣一個觀點绿淋,一個函數(shù)應(yīng)該只做一件事情;該函數(shù)應(yīng)該做好這件事情尝盼;該函數(shù)應(yīng)該做完這件事情吞滞。
掌握這項原則,必須弄明白:怎么叫一件事情盾沫?一件事情裁赠,是指這些事情都在同一個抽象層次上。舉個例子赴精,函數(shù)A把訂單數(shù)據(jù)存到數(shù)據(jù)庫佩捞,并在這個過程中創(chuàng)建券號、發(fā)送短信蕾哟。表面上看一忱,有三件事情,但由于它們都是同一個抽象層次上的事情谭确,所以帘营,實際上,只有一件事情逐哈。如果用創(chuàng)建券號中的代碼來取代創(chuàng)建券號函數(shù)芬迄,函數(shù)A就是做了兩件事情。
此外昂秃,“從上往下“讀函數(shù)(to...do)禀梳,也是區(qū)分函數(shù)是否做了一件事情的好方法。初級程序員要學(xué)會這一技巧肠骆,并不容易出皇。
看函數(shù)的區(qū)段,即一個函數(shù)能否再分拆出其他的函數(shù)哗戈,也是識別函數(shù)是否只做一件事情的方法。
我覺得荷科,根據(jù)”抽象層次“唯咬,就可以判斷函數(shù)是否只做了一件事情。
函數(shù)要區(qū)分指令和詢問畏浆。指令是指函數(shù)所做的事情胆胰,詢問是指函數(shù)回答了了什么問題。一個函數(shù)刻获,要么執(zhí)行指令蜀涨,要么回答詢問,不可同時做這兩件事,否則就違背了“一個函數(shù)應(yīng)該只做好一件事”的原則厚柳。改變類屬性的值是指令氧枣,獲取并返回用戶信息,是詢問别垮;把用戶數(shù)據(jù)存儲到數(shù)據(jù)庫是指令便监,檢測用戶A是否存在是詢問。代碼D碳想。
function isUserExists($userArray){
if $userArray exists{
return true;
}else{
save $userArray
return false;
}
}
上面的函數(shù)就把指令和詢問混合在了一起烧董。
函數(shù)要無副作用。具有副作用的函數(shù)胧奔,違背了“函數(shù)應(yīng)該只做一件事”的原則逊移,甚至還會違背“函數(shù)要區(qū)分指令和詢問”的原則,比如上面的代碼D龙填。具有副作用的函數(shù)胳泉,是隱藏的謊言。請看代碼D觅够,函數(shù)名告訴讀者胶背,它做的事情是檢測一個用戶是否存在。實際上它還在函數(shù)內(nèi)部“偷偷地”存儲了用戶數(shù)據(jù)喘先。不深入函數(shù)去看钳吟,無法知道它的真正功能。具有副作用的函數(shù)窘拯,會帶來一些預(yù)料之外的糟糕后果红且,要避免寫出有副作用的函數(shù)。
函數(shù)的參數(shù)宜少不宜多涤姊。無參數(shù)最佳暇番,一個參數(shù)還好,二個參數(shù)勉強還行思喊,三個甚至更多參數(shù)壁酬,要想辦法避免。參數(shù)代表不同概念恨课,越多舆乔,使用函數(shù)的時候越麻煩。即使有注釋剂公,可能仍然需要看函數(shù)體或其他相關(guān)資料希俩,記憶或弄清參數(shù)的順序,也不容易纲辽。兩個或兩個以上的參數(shù)颜武,測試函數(shù)的時候璃搜,這些參數(shù)會有多少個組合?需要多少測試用例才能完全覆蓋測試鳞上?(這是《代碼整潔之道》中的說法这吻,我不理解)。代碼E因块。
function getColumnValueById($id, column='*'){
$sql = 'SELECT ' . $column . ' FROM tableName WHERE id=' . $id;
$columnValue = execute($sql);
return $columnValue;
}
我可能會給這段代碼寫上注釋
/**
*@author ggh
*@purpose 根據(jù)id從tableName中獲取某個或幾個字段的值
*@return $columnValue
*/
假設(shè)tableName具有username橘原、password等字段,想通過getColumnValueById獲取username的值涡上,根據(jù)代碼并不能知道只需將$column賦值為username就可以了趾断,而是要根據(jù)注釋找到tableName,然后看存儲username的是哪個字段吩愧。將代碼E改寫為代碼F芋酌。
function getUsernameById($id){
$column='username';
$sql = 'SELECT ' . $column . ' FROM tableName WHERE id=' . $id;
$columnValue = execute($sql);
return $columnValue;
}
想查詢username的時候,直接調(diào)用getUsernameById就可以了雁佳,完全不需要去看tableName的字段脐帝。
若采用代碼F風(fēng)格,將產(chǎn)生大量的類似函數(shù)(重復(fù)代碼)糖权。目前堵腹,我是這么解決這個問題的。請看代碼G星澳。
function getColumnValueById($id, column='*'){
$sql = 'SELECT ' . $column . ' FROM tableName WHERE id=' . $id;
$columnValue = execute($sql);
return $columnValue;
}
function getUsernameById($id){
$column='username';
$username =getColumnValueById($id, column)疚顷;
return $username;
}
function getPasswordById($id){
$column='password';
$pwd=getColumnValueById($id, column);
return $pwd;
}
上面的一個簡單例子禁偎,應(yīng)該可以展示少參數(shù)函數(shù)的好處腿堤。那么如何減少函數(shù)的參數(shù)呢?無參數(shù)輸入如暖,函數(shù)的內(nèi)部代碼如何運行笆檀?工作中,我使用的一個類方法A盒至,參數(shù)有十幾個酗洒,而且還在不斷增加〖纤欤《代碼整潔之道》提供了一個方法:當一個函數(shù)需要的參數(shù)超過3個的時候樱衷,就應(yīng)該創(chuàng)建類,把需要的參數(shù)聲明為類屬性登淘。我那個函數(shù)可以用這種思路去減少參數(shù),但會增加很多類屬性封字、設(shè)置類屬性值的方法黔州。調(diào)用A的時候耍鬓,需要調(diào)用同原來A的參數(shù)個數(shù)一樣多的設(shè)置類屬性值的方法。A的參數(shù)從形式上減少了流妻,實質(zhì)仍未發(fā)生變化牲蜀,調(diào)用困難仍然存在。由于類方法A只在一處使用绅这,新增參數(shù)涣达,我只需在舊參數(shù)末尾加一個就可以。如果A被頻繁調(diào)用证薇,我每次都將不得不花一些事件詢問或看注釋這個參數(shù)應(yīng)該賦什么值度苔、那個參數(shù)應(yīng)該賦什么值。
通過創(chuàng)建類來減少函數(shù)參數(shù)的方法浑度,目前我沒有看到它的優(yōu)勢寇窑。編程中,我使用PHP中的數(shù)組來減少函數(shù)的參數(shù)箩张。代碼H甩骏。
function insertUser($username, $pwd, $age, $level){
save user
}
使用數(shù)組
$userArray = array('username'=>$username,'pwd'=>$username, 'age'=>$age,'level'=>$level);
function insertUser($userArray){
$username = $userArray['username'];
$pwd= $userArray['pwd'];
$age= $userArray['age'];
$level= $userArray['level'];
}
將許多參數(shù)放在數(shù)組里,然后將數(shù)組作為一個參數(shù)先慷,的確有效減少了參數(shù)個數(shù)饮笛,使函數(shù)看起來更美觀。僅此而已论熙,其他更多好處福青,有待發(fā)掘。
盡量避免使用輸出參數(shù)赴肚,即在函數(shù)中輸出一個變量素跺。我編程中常使用輸出參數(shù)。這樣可以消除重復(fù)代碼誉券,能使代碼更簡潔指厌。是否應(yīng)該使用輸出參數(shù),需要進一步衡量利弊踊跟。
能寫三篇《代碼整潔之道》讀書復(fù)述筆記踩验,歸功于前段時間看了幾章內(nèi)容。寫完函數(shù)這些章節(jié)商玫,若不抽時間看書箕憾,就無內(nèi)容可寫了。拋開《代碼整潔之道》看來的知識拳昌,寫作幾乎成了無米之炊袭异。可見工作以來炬藤,我掌握的知識御铃、準確地說碴里,透徹地掌握、能清楚地講出來的知識上真,非常少咬腋。