Code Complete II《軟體建構之道 2》#17 讀書心得與整理

第十七章 不常見的流程控制程式結冓
Unusual Control Structures


17.1 常式的多重回傳
Multiple Returns from a Routine


在任何時候離開常式的方法。

return可以提高可讀性的時候
  1. 獲得解答,要立即回傳
  2. 不返回就得寫更多的程式碼跳過別的程式碼
使用防衛子句(guard clause)簡化複雜的錯誤處理
if (A is true)
    if (B is true)
        if (C is true)
            //...
不如改成
if (A isn't true)
    return notA;

if (B isn't true)
    return notB;

if (C isn't true)
    return notC;

//...everything is right
簡潔。但是,在產品代碼中,還須要更多的程式碼來處理這些not的狀況
if (A isn't true)
{
    status = Error_A;
    return notA;
}

if (B isn't true)
{
    status = Error_B;
    return notB;
}

if (C isn't true)
{
    status = Error_C;
    return notC;
}

//...everything is right
更可以讓有相關性的陳述式在一起(Scop縮小),提高可維護性和可讀性。

將每個常式的return數量降低
  • 在增加可讀性時增加數量
  • 在降低可讀性時降低數量


17.2 遞迴
Recursion


可產生優雅的解決方法,就用遞迴
用於小問題,產生簡單、正確的解決方案

遞迴使用心得
Tips for Using Recursion


留一個非遞迴的路徑,當作停止遞迴的出口
防止無限遞迴,考慮使用安全計數器,預防堆疊溢位
多常式遞迴,很危險,請保持在單一常式
使用new創造物件,不要讓它是auto創造出來。
不要使用遞迴運算階乘和數列的問題


17.3 goto()語法
goto


反對goto的論點
The Argument Against gotos

  • 含有goto的程式碼沒有品質
  • 邏輯結構無法利用排版凸顯
  • 編譯器無法最佳化

支持goto的論點
The Argument for gotos

  • goto使程式碼更快或更小
  • 減少重覆的程式碼

關於goto的假辨論
The Phony goto Debate


結論:即使明白所有可以避開goto的程式技巧,但有時候使用goto卻可以提高可讀性和可維護性

goto與錯誤處理時
Error Processing and gotos


goto版
errorState = FileStatus_Success
fileIndex = 0

While ( fileIndex < numFilesToPurge ) And ( errorState = FileStatus_Success )
 
   fileIndex = fileIndex + 1

    If Not ( FindFile( fileList( fileIndex ), fileToPurge ) ) 
    Then
        errorState = FileStatus_FileFindError
        GoTo END_PROC
    End If

    If Not OpenFile( fileToPurge ) 
    Then
        errorState = FileStatus_FileOpenError
        GoTo END_PROC
    End If

    If Not OverwriteFile( fileToPurge ) 
    Then
        errorState = FileStatus_FileOverwriteError
        GoTo END_PROC
    End If

    If Erase( fileToPurge ) 
    Then
        errorState = FileStatus_FileEraseError
        GoTo END_PROC
    End If
End While

DeletePurgeFileList( fileList, numFilesToPurge )
深巢狀if版
errorState = FileStatus_Success
fileIndex = 0

While ( fileIndex < numFilesToPurge ) And ( errorState = FileStatus_Success )
 
   fileIndex = fileIndex + 1

    If FindFile( fileList( fileIndex ), fileToPurge ) 
    Then
        If OpenFile( fileToPurge ) 
        Then
            If OverwriteFile( fileToPurge ) 
            Then
                If Not Erase( fileToPurge ) 
                Then
                    errorState = FileStatus_FileEraseError
                End If
            Else ' couldn\'t overwrite file'
                errorState = FileStatus_FileOverwriteError
            End If
        Else ' couldn\'t open file'
            errorState = FileStatus_FileOpenError
        End If
    Else ' couldn\'t find file'
        errorState = FileStatus_FileFindError
    End If
End While

DeletePurgeFileList( fileList, numFilesToPurge )
Status變數版
errorState = FileStatus_Success
fileIndex = 0

While ( fileIndex < numFilesToPurge ) And ( errorState = FileStatus_Success )

    fileIndex = fileIndex + 1

    If Not FindFile( fileList( fileIndex ), fileToPurge ) 
    Then
        errorState = FileStatus_FileFindError
    End If

    If ( errorState = FileStatus_Success ) 
    Then
        If Not OpenFile( fileToPurge ) 
        Then
            errorState = FileStatus_FileOpenError
        End If
    End If

    If ( errorState = FileStatus_Success ) 
    Then
        If Not OverwriteFile( fileToPurge ) 
        Then
            errorState = FileStatus_FileOverwriteError
        End If
    End If

    If ( errorState = FileStatus_Success ) 
    Then
        If Not Erase( fileToPurge ) Then
            errorState = FileStatus_FileEraseError
        End If
    End If
End While

DeletePurgeFileList( fileList, numFilesToPurge )
try-finally版
Try
    fileIndex = 0
    While ( fileIndex < numFilesToPurge )
        fileIndex = fileIndex + 1
        FindFile( fileList( fileIndex ), fileToPurge )
        OpenFile( fileToPurge )
        OverwriteFile( fileToPurge )
        Erase( fileToPurge )
    Wend
    Finally
        DeletePurgeFileList( fileList, numFilesToPurge )
End Try

寫法  goto版 深巢狀if版 狀態變數版 try-finally版
goto語法 可避開 可避開 可避開
巢狀if的語法 可避開 可避開 可避開
其它 避開額外測試 產生複雜的程式碼 需加入額外測試 並不是每個語言都支援


goto與共享在else裡的代碼
gotos and Sharing Code in an else Clause


goto版
if ( statusOk ) {
    if ( dataAvailable ) {
        importantVariable = x;
        goto MID_LOOP;
    }
}
else {
    importantVariable = GetValue();

    MID_LOOP:

    // lots of code
    ...
}
呼叫常式版
if ( statusOk )
{
    if ( dataAvailable )
    {
        importantVariable = x;
        DoLotsOfCode( importantVariable );
    }
}
else
{
    importantVariable = GetValue();
    DoLotsOfCode( importantVariable );
}
重建條件式版
if ( ( statusOk && dataAvailable ) || !statusOk )
{
    if ( statusOk && dataAvailable )
    {
        importantVariable = x;
    }
    else
    {
        importantVariable = GetValue();
    }

// lots of code
...
}

goto的使用原則和心得
Summary of Guidelines for Using gotos


goto的使用與否取決於信仰。
goto()代表效率。

17.4 剖析不常見的流程控制程式結冓
Perspective on Unusual Control Structures


沒有留言:

張貼留言

(什麼是留言欄訊息?)