// 上一篇:分枝/葉子(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語句,可以讓代碼更加線性化。具體在情景代碼中應該使用哪種方式,就是一種編程中的選擇問題。如果考慮一致性,最好一個模塊保持一致。