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