1. 變數存留時間的思考
Code complete提出了兩個概念一個是span(擴度)另一個是live time(存留時間)用可以量化的東西去衡量代碼的好壞。那這兩個概念是什麼含義了?看一個例子:
a = 0;
b = 0;
c = 0;
a = b + c;
可以看到a在第一行和第4行出現,那麼a的span就是2.b同理span是1,c是0.對於span的大小對程式有何影響了?如果span很小的話,那麼變數的賦值和運算就會相對集中,那麼看代碼的人會把注意力集中於這一小段,不用回頭去找變數的聲明或者初值,方便了看代碼者,同時也方便了查錯和程式的修改。
什麼是live time了?這個概念不是說的傳統意義上的變數的存留時間。一個變數生命的開始是在第一個引用該變數的語句處,結束與最後一個引用它的語句處。看下面的圖:
清楚的解釋了live time和span的概念。同樣live time如果很小的話,對於某一個變數就可以僅僅關注與那一小段代碼,比如一個變數只在44和45行用到,那麼只用關注兩行代碼。如果它在10行和45行用它,那麼之間的34行代碼你都必須查看,以防變數被意外修改。可見live time的減少也是增加了代碼可讀性,減少出錯的可能,方便查錯和修改。
如何減少這兩個值很簡單,一是注意在使用時再賦值,二是注意在迴圈之前就對要用到的變數賦值,而不是在包含這個迴圈的函數開頭對變數賦值。這樣做是因為如果對迴圈A做了修改,如有又有一個迴圈B在那麼初始化就不僅針對第一個迴圈A進行初始化,同時也對B進行初始化。
以上我想也解釋了為什麼程式要盡量少使用全域變數的原因了。使用了全域變數對於編程人員來說是非常方便,資料可以隨便讀取和修改,使用方便。但是對於其他人來讀或者修改查錯的話就非常痛苦了。如果想要看懂其中的一個函數必須得把其它函數都看懂,因為耦合太緊了。而用封裝很好的函數,僅使用live time和span都短的變數,就會讓看代碼的人很輕鬆,以後的修改和維護也方便。不過這也從某種程度上增加了程式的複雜,不過肯定比滿是全域變數的程式要好的多。
2. 邏輯相關操作放在一起
首先看下面一個例子:
void SummarizeData (...) {
...
GetOldData( oldData, &numOldData );
GetNewData( newData, &numNewData );
totalOldData = Sum( oldData, numOldData );
totalNewData = Sum( newData, numNewData );
PrintOldDataSummary( oldData, totalOldData, numOldData );
PrintNewDataSummary( newData, totalNewData, numNewData );
SaveOldDataSummary( totalOldData, numOldData );
SaveNewDataSummary( totalNewData, numNewData );
...
}
可以看到這個程式就寫的很爛,沒有把邏輯相關的操作放在一起。Old和new交叉使用,看的人心情肯定不大好,不過修改起來很方便。
void SummarizeDaily( ... ) {
GetOldData( oldData, &numOldData );
totalOldData = Sum( oldData, numOldData );
PrintOldDataSummary( oldData, totalOldData, numOldData );
SaveOldDataSummary( totalOldData, numOldData );
...
GetNewData( newData, &numNewData );
totalNewData = Sum( newData, numNewData );
PrintNewDataSummary( newData, totalNewData, numNewData );
SaveNewDataSummary( totalNewData, numNewData );
...
}
這樣邏輯就清晰很多。這其實本質上和之前講的變數live time和span是一樣的,就是一次只做一件事情,讓人的精力集中一點,這樣感覺思路會很清晰。寫程式和做事的道理是一樣的。