1 函數(shù)的功能與規(guī)模設(shè)計(jì)(1)函數(shù)應(yīng)當(dāng)短而精美,而且只做一件事。
不要設(shè)計(jì)多用途面面俱到的函數(shù),多功能集于一身的函數(shù),很可能使函數(shù)的理解、測試、維護(hù)等變得困難。 一個函數(shù)應(yīng)最多占滿1或2個屏幕(就象我們知道的那樣,ISO/ANSI的屏幕大小是80X24),只做一件事并且把它做好。
一個函數(shù)的最大長度與它的復(fù)雜度和縮進(jìn)級別成反比。所以,如果如果你有一個概念上簡單(案,“簡單”是simple而不是easy)的函數(shù),它恰恰包含著一個很長的case語句,這樣你不得不為不同的情況準(zhǔn)備不懂的處理,那么這樣的長函數(shù)是沒問題的。
然而,如果你有一個復(fù)雜的函數(shù),你猜想一個并非天才的高一學(xué)生可能看不懂得這個函數(shù),你就應(yīng)當(dāng)努力把它減縮得更接近前面提到的最大函數(shù)長度限制。可以使用一些輔助函數(shù),給它們?nèi)∶枋鲂缘拿?如果你認(rèn)為這些輔助函數(shù)的調(diào)用是性能關(guān)鍵的,可以讓編譯器把它們內(nèi)聯(lián)進(jìn)來,這比在單個函數(shù)內(nèi)完成所有的事情通常要好些)。
對函數(shù)還存在另一個測量標(biāo)準(zhǔn):局部變量的數(shù)目。這不該超過5到10個,否則你可能會弄錯。應(yīng)當(dāng)重新考慮這個函數(shù),把它分解成小片。人類的大腦一般能同時記住7個不同的東西,超過這個數(shù)目就會犯糊涂?;蛟S你認(rèn)為自己很聰明,那么請你理解一下從現(xiàn)在開始的2周時間你都做什么了。
(2)為簡單功能編寫函數(shù)。
雖然為僅用一兩行就可完成的功能去編函數(shù)好象沒有必要,但用函數(shù)可使功能明確化,增加程序可讀性,亦可方便維護(hù)、測試。 示例:如下語句的功能不很明顯。
value = ( a > b ) ? a : b ;改為如下就很清晰了。
int max (int a, int b)
{
return ((a > b) ? a : b);
}
value = max (a, b);或改為如下。
#define MAX (a, b) (((a) > (b)) ? (a) : (b))
value = MAX (a, b);
當(dāng)一個過程(函數(shù))中對較長變量(一般是結(jié)構(gòu)的成員)有較多引用時,可以用一個意義相當(dāng)?shù)暮甏妗@樣可以增加編程效率和程序的可讀性。 示例:在某過程中較多引用TheReceiveBuffer[FirstSocket].byDataPtr,則可以通過以下宏定義來代替:# define pSOCKDATA TheReceiveBuffer[FirstScoket].byDataPtr
(3)防止把沒有關(guān)聯(lián)的語句放到一個函數(shù)中,防止函數(shù)或過程內(nèi)出現(xiàn)隨機(jī)內(nèi)聚。
隨機(jī)內(nèi)聚是指將沒有關(guān)聯(lián)或關(guān)聯(lián)很弱的語句放到同一個函數(shù)或過程中。隨機(jī)內(nèi)聚給函數(shù)或過程的維護(hù)、測試及以后的升級等造成了不便,同時也使函數(shù)或過程的功能不明確。使用隨機(jī)內(nèi)聚函數(shù),常常容易出現(xiàn)在一種應(yīng)用場合需要改進(jìn)此函數(shù),而另一種應(yīng)用場合又不允許這種改進(jìn),從而陷入困境。
在編程時,經(jīng)常遇到在不同函數(shù)中使用相同的代碼,許多開發(fā)人員都愿把這些代碼提出來,并構(gòu)成一個新函數(shù)。若這些代碼關(guān)聯(lián)較大并且是完成一個功能的,那么這種構(gòu)造是合理的,否則這種構(gòu)造將產(chǎn)生隨機(jī)內(nèi)聚的函數(shù)。
示例:如下函數(shù)就是一種隨機(jī)內(nèi)聚。
void Init_Var( void )
{
Rect.length = 0;
Rect.width = 0; /* 初始化矩形的長與寬 */
Point.x = 10;
Point.y = 10; /* 初始化“點(diǎn)”的坐標(biāo) */
}
矩形的長、寬與點(diǎn)的坐標(biāo)基本沒有任何關(guān)系,故以上函數(shù)是隨機(jī)內(nèi)聚。應(yīng)如下分為兩個函數(shù):
void Init_Rect( void )
{
Rect.length = 0;
Rect.width = 0; /* 初始化矩形的長與寬 */
}
void Init_Point( void )
{
Point.x = 10;
Point.y = 10; /* 初始化“點(diǎn)”的坐標(biāo) */
}
(4)如果多段代碼重復(fù)做同一件事情,那么在函數(shù)的劃分上可能存在問題。若此段代碼各語句之間有實(shí)質(zhì)性關(guān)聯(lián)并且是完成同一件功能的,那么可考慮把此段代碼構(gòu)造成一個新的函數(shù)。
(5)減少函數(shù)本身或函數(shù)間的遞歸調(diào)用。遞歸調(diào)用特別是函數(shù)間的遞歸調(diào)用(如A->B->C->A),影響程序的可理解性;遞歸調(diào)用一般都占用較多的系統(tǒng)資源(如棧空間);遞歸調(diào)用對程序的測試有一定影響。故除非為某些算法或功能的實(shí)現(xiàn)方便,應(yīng)減少沒必要的遞歸調(diào)用,對于safe-related 系統(tǒng)不能用遞歸,因?yàn)槌龆褩?臻g很危險。
2 函數(shù)的返回值(1)對于函數(shù)的返回位置,盡量保持單一性,即一個函數(shù)盡量做到只有一個返回位置。(單入口單出口)。
要求大家統(tǒng)一函數(shù)的返回值,所有的函數(shù)的返回值都將以編碼的方式返回。
例如編碼定義如下:
#define CM_POINT_IS_NULL CMMAKEHR(0X200)
:
:
建議函數(shù)實(shí)現(xiàn)如下:
LONG 函數(shù)名(參數(shù),……)
{
LONG lResult; //保持錯誤號
lResult=CM_OK;
//如果參數(shù)有錯誤則返回錯誤號
if(參數(shù)==NULL)
{
lResult=CM_POINT_IS_NULL;
goto END;
}
……
END:
return lResult;
}
(2)除非必要,最好不要把與函數(shù)返回值類型不同的變量,以編譯系統(tǒng)默認(rèn)的轉(zhuǎn)換方式或強(qiáng)制的轉(zhuǎn)換方式作為返回值返回。
(3)函數(shù)的返回值要清楚、明了,讓使用者不容易忽視錯誤情況。函數(shù)的每種出錯返回值的意義要清晰、明了、準(zhǔn)確,防止使用者誤用、理解錯誤或忽視錯誤返回碼。
(4)函數(shù)的功能應(yīng)該是可以預(yù)測的,也就是只要輸入數(shù)據(jù)相同就應(yīng)產(chǎn)生同樣的輸出。帶有內(nèi)部“存儲器”的函數(shù)的功能可能是不可預(yù)測的,因?yàn)樗妮敵隹赡苋Q于內(nèi)部存儲器(如某標(biāo)記)的狀態(tài)。這樣的函數(shù)既不易于理解又不利于測試和維護(hù)。在C/C++語言中,函數(shù)的static局部變量是函數(shù)的內(nèi)部存儲器,有可能使函數(shù)的功能不可預(yù)測,然而,當(dāng)某函數(shù)的返回值為指針類型時,則必須是STATIC的局部變量的地址作為返回值,若為AUTO類,則返回為錯針。
示例:如下函數(shù),其返回值(即功能)是不可預(yù)測的。
unsigned int integer_sum( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意,是static類型的。
// 若改為auto類型,則函數(shù)即變?yōu)榭深A(yù)測。
for (index = 1; index <= base; index++)
{
sum += index;
}
return sum;
}
3 函數(shù)參數(shù)(1)只當(dāng)你確實(shí)需要時才用全局變量,函數(shù)間應(yīng)盡可能使用參數(shù)、返回值傳遞消息。
(2)防止將函數(shù)的參數(shù)作為工作變量。將函數(shù)的參數(shù)作為工作變量,有可能錯誤地改變參數(shù)內(nèi)容,所以很危險。對必須改變的參數(shù),最好先用局部變量代之,最后再將該局部變量的內(nèi)容賦給該參數(shù)。
示例:下函數(shù)的實(shí)現(xiàn)不太好。
void sum_data( unsigned int num, int *data, int *sum )
{
unsigned int count;
*sum = 0;
for (count = 0; count < num; count++)
{
*sum += data[count]; // sum成了工作變量,不太好。
}
}
若改為如下,則更好些。
void sum_data( unsigned int num, int *data, int *sum )
{
unsigned int count ;
int sum_temp;
sum_temp = 0;
for (count = 0; count < num; count ++)
{
sum_temp += data[count];
}
*sum = sum_temp;
} |
|