控制結構(2): 衛語句(guard clause)


// 上一篇:分枝/葉子(branch/leaf)
// 下一篇:狀態機(state machine)


基於語言提供的基本控制結構,更好地組織和表達程序,需要良好的控制結構。

典型代碼:

  • 同步版本
function loadFunc(funcInfo){
    if(funcInfo){
        let funcObj = doParserFunc(funcInfo);
        if(funcObj){
            let package = doLoadPackage(funcObj.packageName);
            if(pacakge){
                let module = doLoadModule(pacakge,funcObj.moduleName);
                if(module){
                    let func = module[funcObj.functionName];
                    if(func){
                        return func;
                    }else{
                        // do something
                    }
                }else{
                    // do something
                }
            }else{
                // do something
            }
        } else {
            // do something
        }
    }
    return null;
}
  • 異步版本
function loadFunc(funcInfo, onComplete){
    if(funcInfo){
        let funObj = doParseFunc(funcInfo);
        if(funcObj){
            doLoadPackage(funcObj.packageName, function(err,package){
                if(package){
                    doLoadModule(package, funcObj.moduleName, function(err, module){
                        if(module){
                            let func = module[funcObj.functionName];
                            if(func){
                                onComplete(0,func);
                            }else{
                                onComplete(1);
                            }
                        }else{
                            onComplete(1);
                        }
                    })
                }else{
                    onComplete(1);
                }
            });
        }else{
            onComplete(1);
        }
    }else{
        onComplete(1);
    }
}

結構分析

無論是同步版本,還是異步版本,都存在嵌套持續變深的問題。隨着開發的進行,需求的變更,代碼會變的越發繁雜。一種方式是通過上一節的方式,合理組織函數的分層,讓函數的組織表達更清晰。但是另一方面,在支持lambda表達式和匿名函數的語言里,編程的時候總是會大量使用語言提供的這種便利,寫帶有許多lambda表達式或匿名函數的邏輯代碼。一種常見的方式是使用衛語句(guard clause)的方式提前返回,減少嵌套。

  • 同步版本
function loadFunc(funcInfo){
    if(!funcInfo){
        return null;
    }

    let funcObj = doParserFunc(funcInfo);
    if(!funcObj){
        return null;
    }

    let package = doLoadPackage(funcObj.packageName);
    if(!package){
        return null;
    }

    let module = doLoadModule(pacakge,funcObj.moduleName);
    if(!module){
        return null;
    }

    let func = module[funcObj.functionName];
    if(!func){
        return null;
    }

    return func;
}
  • 異步版本
function loadFunc(funcInfo, onComplete){
    if(!funcInfo){
        return onComplete(RESULT.INVALID_PARAMETER);
    }

    let funcObj = doParserFunc(funcInfo);
    if(!funcObj){
        return onComplete(RESULT.PARSE_ERROR);
    }

    
    doLoadPackage(funcObj.packageName, function(err,package){
        if(!package){
            return onComplete(RESULT.LOAD_PACKAGE_FAILED);
        }

        doLoadModule(package, funcObj.moduleName, function(err, module){
            if(!module){
                return onComplete(RESULT.LOAD_MODULE_FAILED);
            }
            
            let func = module[funcObj.functionName];
            if(!func){
                return onComplete(RESULT.FUNC_NOT_EXIST);
            }

            onComplete(RESULT.SUCCESS,func);
        });
    });
}

語義分析

編程語言提供的if/else結構,有兩種基本的用法:

  • 優先考慮滿足條件就做什么
if(condition){
    doSomething();
}else{
    logError();
}
  • 優先考慮不滿足條件就處理錯誤
if(!condition){
    logError();
}else{
    doSomething();
}

這兩種結構,各自都能寫出邏輯嚴密的代碼。例如,在前一種優先模式下,一種重要的方式把所有的if分支都寫下它的else分支,保證邏輯上少漏洞。典型的控制結構是這樣的:

if(condition1){
    // <a>
    if(condition2){
        // <b>
        if(condition3){
            // <c>
            if(condition4){
                // <d>
                doSomething1();
            }else{
                // <e>
                doSomething2();
            }
        }else{
            // <f>
            doSomething3();
        }
    }else{
        // <g>
        doSomething4();
    }
}else{
    // <h>
    doSomething5();
}

這種結構確實也能更直接的和大腦里的流程結構對上,如果勤快一點畫流程圖,也能直接對上。程序是會根據需求變化的,在需求變化的時候,很容易在上述<a><b>, ...處產生碎片代碼,此時如果對上一節介紹的函數分層有比較好的實施,則代碼依然保持良好的可讀/可維護。

但是,在混合了同步、異步之后,即使有了良好的函數組織,也還是容易出現嵌套深的情況,此時,可以配合適當的guard結構去組織代碼。使用guard語句,可以讓代碼更加線性化。具體在情景代碼中應該使用哪種方式,就是一種編程中的選擇問題。如果考慮一致性,最好一個模塊保持一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM