天天看點

高品質的子程式1

建立子程式最主要的目的是提高程式的可管理性,其次,節省代碼空間也隻是一個次要原因:提高可讀性,可靠性和可修改性等原因都更重要一些。

我們先來看一個低品質的子程式的例子:

void HandleStuffle( CORP_DATA & inputRec, int crntQtr, EMP_DATA empRec, double & estimRevenue, double 
ytdRevenue, int screenX, int screenY, COLOR_TYPE & newColor, COLOR_TYPE & prevColor, StatusType & status, int expenseType )
{
int i;
for ( i = 0; i < 100; i++ ) {
    inputRec.revenue[i] = 0;
    inputRec.expense[i] = corpExpense[crntQtr][i];
}
UpdateCorpDatabase( empRec );
estimRevenue = ytdRevenue * 4.0 / (double) crntQtr;
newColor = prevColor;
status = SUCCESS;
if ( expenseType == 1){
      for (i = 0; i < 12; i++ )
           profit[i] = revenue[i] - expense.type1[1];
      }
else if ( expenseType == 2){
           profit[i] = revenue[i] - expense.type2[1];
         }
else if ( expenseType == 3){
           profit[i] = revenue[i] - expense.type3[1];
        }
}
           

以下列出該段代碼存在的問題:

  1. 子程式有個差勁的名字。讓人一點兒也看不出這個子程式究竟是做什麼的;
  2. 沒有說明文檔;
  3. 布局不好,過于随意;
  4. 這個子程式的輸入變量 inputRec 的值被改變了。如果它是一個輸入變量,她的值就不應該被修改(而且在C++中它應該被定義為 const)。如果變量的值就是要被修改的,那就不要把它命名為 inputRec;
  5. 這個子程式讀寫了全局變量,它從 corpExpense 中讀取數值,寫入 profit 。它應該更直接地與其他子程式通信,而不是去讀寫全局變量。
  6. 沒有單一的目的。它初始化了一些變量,向資料庫寫入資料,又做了一些計算。從這些事情之間看不出任何聯系。子程式應該有單一而明确的目的。
  7. 沒有注意防範錯誤資料。如果 crntQtr = 0,那麼表達式 ytdRevenue * 4.0 / (double) crntQtr 将會導緻除零錯誤。
  8. 使用了若幹神秘數值。100,  4.0,  12,  2,  3 等。
  9. 未使用其中一些參數:screenX 和 screenY 都沒有被引用過。
  10. 一個參數傳遞方式有誤:prevColor 被标為引用參數,單在這個字程式内卻未對其指派。
  11. 參數太多。合理的參數個數,其上限大概是 7 個左右,而這裡有 11 個。而且參數的排布方式也難以了解。
  12. 參數順序混亂且未經注釋。

建立子程式的正當理由:

  1. 降低複雜度。
  2. 引入中間,易懂的抽象。把一段代碼放入一個命名恰當的子程式内,是說明這段代碼用意最好的方法之一。被良好命名的子程式提供了更高層次的抽象,進而使代碼更具可讀性,也更容易了解,同僚也降低了原來包含代碼的程式的複雜度。
  3. 避免代碼重複。這是建立子程式最普遍的原因。
  4. 支援子類化。覆寫(override)簡短而規整的子程式所需新代碼的數量,要比覆寫冗長而邋遢的子程式更少。
  5. 隐藏順序。把處理事件的順序隐藏起來是個好主意,比讓它們在系統内到處散步要好很多。
  6. 隐藏指針操作。
  7. 提高可移植性。
  8. 簡化複雜的布爾判斷。
  9. 改善性能。

内聚性:

對于子程式來說,内聚性是指子程式中各種操作之間聯系的緊密程度。像 Cosine() (餘弦函數)這樣 的函數就是極端内聚的,因為整個程式隻完成一項功能。而 consineAndTan()(餘弦與正切)這個函數的内聚性相對較弱,因為它完成了多于一項的操作。

關于内聚性的幾個層次:

  1. 功能的内聚性。是最強也是最好 的一種内聚性,也就是說讓一個子程式僅執行一項操作。
  2. 順序上的内聚性。指子程式内包包含有需要按的定順序執行的操作,這些步驟需要共享資料,而且隻有在全部執行完畢後才完成了一項完整的功能。
  3. 通信上的内聚性。指一個子程式的不同操作使用了同樣的資料,但不存在其他任何聯系。
  4. 臨時的内聚性。含有一些因為需要同時執行才放到一起的操作的子程式。典型的例子有:Startup(), Shutdown() 等。

繼續閱讀