##Chapter7
##提高控制流程可讀性
###重要概念
讓所有的條件判斷、迴圈與其他改變流程的敘述"盡量自然"
--用不需要使用者停下來重讀的方式撰寫程式碼
###條件式中的流程順序(1/2)
if (length >= 10)
或
if (10 <= length)
###條件式中的流程順序(2/2)
while (byte_received < byte_expected)
或
while (byte_expected > byte_received)
左側|右側
------------- | --------------
代表比較對象|代表比較基準
數值較有變化|數值大多是固定常數
###尤達表示法
為了避免
if ($obj = null)
所以改成
if (null == $obj)
_現代編譯器都會產生警告?_
###if/else區塊順序
* 先處理肯定條件
* 先處理簡單的情況
* 先處理比較有趣或明顯的情況
###要你不要想"粉紅色大象"!
if (uri.HasQueryParameter("expand_all") != true) {
response.Render(items);
} else {
for (int i = 0; i < item.size() ; i++) {
item[i].Expand();
}
}
### 先處理簡單的情況
if (!is_login) {
// not login
}
else {
// do something
}
###避免 do/while 迴圈
// 從 node 開始搜尋,尋找指定的 name
// 不考慮大於 max_length 之後的節點
public boolean ListHasNode(Node node, String name, int max_length) {
do {
if (node.name().equals(name))
return true;
node = node.next();
} while (node != null && --max_length > 0);
return false;
}
###為了避免 do/while
body
while (condition) {
body again
}
蠢
###多數 do/while 可以改成 while
public boolean ListHasNode(Node node, String name, int max_length) {
while (node != null && max_length-- > 0) {
if (node.name().equals(name)) return true;
node = node.next();
}
return false;
}
###避免在 do/while 裡面用 continue
do {
continue;
} while (false);
###儘早由函數中返回
public boolean Contains(String str, String substr) {
if (str == null || substr == null) return false;
if (substr.equals("")) return true;
}
###減少巢狀結構
if (user_result == SUCCESS) {
if (permission_result != SUCCESS) {
reply.WriteErrors("error reading permissions");
reply.Done();
return;
}
reply.WriteErrors("");
} else {
reply.WriteErrors(user_result);
}
reply.Done();
###用儘早返回函數消除巢狀結構
if (user_result != SUCCESS) {
reply.WriteErrors(user_result);
reply.Done();
return;
}
if (permission_result != SUCCESS) {
reply.WriteErrors(permission_result);
reply.Done();
return;
}
reply.WriteErrors("");
reply.Done();
###消除迴圈中的巢狀結構
for (int i=0; i < result.size();i++) {
if(result[i] != NULL) {
non_null_count++;
if (result[i]->name != "") {
cout << "Considering candidate.."<
###用continue去解
for (int i=0; i < result.size();i++) {
if(result[i] == NULL) continue;
non_null_count++;
if (result[i]->name == "") continue;
cout << "Considering candidate.."<
###Chapter 7 結語
* 比較時,最好將會變動的數直放左側,固定不變得放右側
* if/else順序: 先處理肯定/容易/有趣
* 盡可能減少巢狀結構
##Chapter 8
##分解巨大表示式
###解釋性變數 (explaining variable)
if (line.split(':')[0].strip() == "root")
user_name = line.split(':')[0].strip();
if (user_name == "root")
###摘要變數 (summary variable) (1/2)
if (request.user.id == document.owned_id) {
// 使用者可以編輯
}
...
if (request.user.id != document.owned_id) {
// 文件狀態是唯讀
}
###摘要變數 (summary variable) (2/2)
final boolean user_owns_document = (request.user.id == document.owned_id);
if (user_owns_document) {
// 使用者可以編輯
}
...
if (user_owns_document) {
// 文件狀態是唯讀
}
###利用笛摩根定律 (De Morgan's law)
1. not (a or b or c) <=> (not a) and (not b) and (not c)
2. not (a and b and c) <=> (not a) or (not b) or (not c)
if(!(file_exists && !is_protected)) {
Error("Sorry, could not read file");
}
v.s.
if(!file_exists || is_protected) {
Error("Sorry, could not read file");
}
###誤用捷徑邏輯
assert((!(bucket = Findbucket(key))) || !bucket->IsOccupied)
v.s.
bucket = Findbucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
###與複雜邏輯搏鬥
struct Range{
int begin;
int end;
bool OverlapsWith(Range other);
};
![Image of Overlaps](http://www.codeproject.com/KB/cs/DateRangeOverlapTesting/range_problem.jpg)
###實作OverlapsWith--檢查兩個段點是否位於other的範圍中
bool Range::OverlapsWith(Range other) {
return (begin >= other.begin && begin < other.begin) ||
(end < other.begin && end <= other.end) ||
(begin <= other.begin && end >= other.end);
}
###從"相反方向解決問題"
bool Range::OverlapsWith(Range other) {
if (other.end <= begin) return false; // 他們在我們開始之前就結束
if (other.begin >= end) return false; // 他們在我們結束之後才開始
return true;
}
###分解巨大的敘述(1/2)
void update_highlight = function (message_num) {
if ($("#vote_value" + message_num).html() === "Up") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + message_num).html() === "Down") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
}
###分解巨大的敘述(1/2)
void update_highlight = function (message_num) {
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + message_num).html();
var hi = "highlighted";
if (vote_value === "Up") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "Down") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
}
###Chapter 8 結語
* 解釋性變數好處
* 分解巨大的表示式
* 以有意義的名稱作為程式碼的說明
* 笛摩根定律
* 反轉問題或是考慮相反的目標