星期日, 4月 18, 2010

Code Complete 2

作者:Steve McConnell

Ch6 工作類別

類別基礎:抽象資料型別(ADT)
將字型大小變更為12點,即16個像素高
//易混淆
currentFont.size = 16; //不知是pixel還是點

//較具意義
currentFont.size = PointsToPixels(12); 
currentFont.bold = true;
currentFont.setBoldOn();

Ch7 高品質常式

從常式名稱中可猜出其作用
單一且明確定義的目的
參數不易超過7個,造成閱讀不易
具備抵禦錯誤資料,不會造成當機

良好的常式名稱
  • 描述常式的所有工作
  • 避免無意義、模糊式空泛的動詞命令
  • FormatAndPrintOut()會比HandleOutput()更加的清楚
  • 勿僅用數字后別常式名稱
  • part1(),part2()
  • 用回傳值述命名函式
  • printer.isReady(),user.next()
如何使用常式參數
  • 文件化有關參數的介面假設
  • 不要回頭再寫註解,數字參考單位,預期值的範圍、永不應出現的特定值
  • 考慮輸入、修改和輸出參數的命名慣例
  • input_,modify_,output_

Ch8 防禦性程式設計

用判斷提示證明和驗證前置條件及後置條件
又稱為design by contract (Meyer 1997)
function velocity(float latitude, float longitude,float elevation){
//Preconditions
Debug.Asert ( -90 <= latitude && latitude <= 90); 
Debug.Assert ( 0 <= longitude && longitude<= 360); 
Debug.Assert ( -500 <= elevation && elevation <= 75000); ... 

//Postcondition 
Debug.Assert()0 <= returnVelocity && returnVelocity <= 600);  
//return value 
return returnVelocity ; 
}

對於錯誤的條件,常式通常會用判斷提示直接處理錯誤的程式碼
判斷提示
Debug.Assert()0 <= returnVelocity && returnVelocity <= 600); 

直接處理錯誤的程式碼
...
//cloest legal value
if(latitude <-90) latitude = -90; else if (latitude > -90)
latitude = 90;
...

8.3 處理錯誤的技巧
傳回中性值
有時對錯誤資料的最佳反應就是持續操作,回傳預設值。數字計算=>0,文字操作=>空字串
關鍵情況下,就回傳錯誤,不要回傳錯誤的資料。
以下一個有效資料取代
處理資料串流時
傳回與上一次相同的回答
考慮建立集中的例外況報告程式
確保例外狀況處理一致性,也能提供中央儲存機制九
function reportException(string className, Exception ex){
string caption="Exception";
string message = 
"Exception: " + ex.Message + Enviroment.NewLine +
"Class: " + className + Enviroment.NewLine +
"Routine: " + ex.TargetSite.Name;

MessageBox.Show(message,caption);
}

//example
try{
...
}catch(Exception ex){
reportException(CLASSNAME,ex);
}

8.5 建立路障,避免程式內含錯誤造成的損毀
撰寫驗證類別,在所有介面運算資料前,透過驗證類別確保資料有效

Ch14 組織直線
14.1.須依特定順序排列的陳述式
1.連續呼叫相依的methods
//計算年營收前,得先計算季營收,而計算季營收前,要先計算月營收
revenue.ComputeMonthly(); //
revenue.ComputeQuarterly();
revenue.ComputeAnnual();
相依性不明顯,將來順序對換,會造成錯誤, 要讓程式碼可看出其相依關係
 2.初始化
//求各部門支出前,要先利用ComputeMarketingExpense()初始化
ComputeMarketingExpense(); //
ComputeSalesExpense();
ComputeTravelExpense();
無法在第一時間看出計算前要先初始化,將來順序對換會錯誤

使常式參數使相依性明顯 
//利用初始化及輸出值,提示相依性
expenseData = InitializeExpenseData(expenseData);

expenseData = ComputeMonthly(expenseData);
expenseData = ComputeQuarterly();
expenseData = ComputeAnnual();  

Ch15 使用條件式
15.1 if
指導方針
  • 先寫pass判斷的正常路徑;再寫異常案例後,避免模糊掉執行邏輯
  • 將正常的case置於if block, 而非else後方
OpenFile(inputFile, status); 
if( status = Status_Success )  //pass
    ReadFile( inputFile, fileData, status); //名目案例
    if(status = Status_Success)
    { 
        end(); 
        errorType= ErrorType_None;
    }
    else
        errorType = ErrorType_FileReadError; //錯誤案例
else
    errorType = FileOpenError; //錯誤案例
重點在讀取主要流程,而非費力專研例外案例
Ch26. 程式碼微調技巧
26.2 迴圈
決策外置(Unswitching)
如果if判斷不會在迴圈執行時變更,就把if搬到迴圈外

合併
將兩個相同集合項目的迴圈合併,一次就把兩次事做完
for(i=0;i<n;i++)
  a++;
for(i=0;i < n;i++)
  b++;
------------------------合併成------------------------
for(i=0;i < n;i++){
  a++;
  b++;
}

減少迴圈內的工作
要使迴圈有效率,其中的關鍵在減少迴圈內部的工作。
如果可在外部運算部份或全部的陳述式,只讓結果進入迴圈內,就可提升效率

26.3 資料轉換
使用整數而非浮點數
盡量不要使用陣列維度
減少陣列參考
利用變數存陣列值,就可避免在迴圈內讀取陣列值
for(...)
  rate[level] *= discount[type]; //discount[type]可抽出來
---------------------用變數取代---------------------------------
discounttype =  discount[type];
for(...)
  rate[level] *=  discounttype;

26.4 運算式
利用代數恆等式
not a and not b = not (a or b)
後者會比前者快上將近1份的時間
使用強度降代
  • 用加法取代乘法
  • 用乘法取代乘冪
  • 用三角恆等式取代三角常式
  • 整數運算會比浮點數快
  • 使用位移運算取代整數乘除

沒有留言: