如何写好流程控制语句(if-else/switch/while/for)使得代码更可读些?(chap 7)
* 提高条件语句的可读性(if语句, 或者bool型summary变量)
if (length >= 10) // Good if (10 <= length) // Bad while (bytes_received < bytes_expected) // Good while (bytes_expected < bytes_received) // Bad if (NULL == obj) ... // Not suggested now if (obj == NULL) ... // Good
分析:
left-hand side放我们需要检查的, 经常变化的变量
right-hand side放常量或者不容易变化的值,这样更容易阅读和理解
* 考虑if/else语句块的顺序
注意:
- prefer positive case first instead of negative
- prefer simple case first to get it out of the way
- prefer more interesting or conspicuous case first
e.g.例外
if not file:
# log the error ...
else:
# ...
* 条件运算符(conditional expression, ?:)
time_str += (hour >= 12) ? "pm" :"am"; // Bad if (hour >= 12) { // Good: more clear time_str += "pm"; } else { time_str += "am"; }
key idea: 与其减少代码的行数,不如减少别人理解代码所花的时间.
return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent); // Bad if (exponent >= 0) { // Good return mantissa * (1 << exponent); } else { return mantissa / (1 << -exponent); }
* 避免do/while循环,避免goto语句
* 让函数提前退出
* 减少折叠的层数
理由: 层数越深,别人阅读代码就越复杂,每深入/退出一层,别人需要在脑中进行入栈/出栈的操作
方法: 当你在已有的代码上做修改时,请试图从全新的角度审视这段代码. 往回看, 把之前修改和
现在需要做的修改当成一个整体考虑.
技巧:
1. 及时返回以减少嵌套的层数.
2. 循环中用continue减少嵌套
if (results[i] != NULL) { // Bad .... } if (results[i] == NULL) continue; // Good
* 其他能导致代码很难读的流程控制,需要特别注意
1. 线程/进程/异常处理/信号量
2. 函数指针/匿名函数/虚函数
如何将长而大的表达式化小?(chap 8)
* 解释型变量(Explaining variables)
// Bad : too long, hard to grasp meaning if line.split(':')[0].strip() == "root": ... // Good : clear enough, understand by the variable name username = line.split(':')[0].strip() if username == "root": ...
* 总结型变量(summary variables)
// Bad : five variables in a line if (request.user.id == document.owner_id) { // user can edit this document... } ... if (request.user.id != document.owner_id) { // document is read-only... } // Good : easy to catch the meaning by variable name final boolean user_owns_document = (request.user.id == document.owner_id); if (user_owns_document) { // user can edit this document... } ... if (!user_owns_document) { // document is read-only... }
* 善用德摩根定律提高条件表达式的可读性 (De morgan's Laws)
1) !(a || b || c) <==> (!a) && (!b) && (!c)
2) !(a && b && c) <==> (!a) || (!b) || (!c)
// Bad if (!(file_exists && !is_protected)) Error("Sorry, could not read file."); // Good : clear logic if (!file_exists || is_protected) Error("Sorry, could not read file.");
* 不要滥用短路逻辑(short-circuit logic)
// Bad : not clear assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied()); // Good : clear enough bucket = FindBucket(key); if (bucket != NULL) assert(!bucket->IsOccupied()); // except: 可以使用下面这种风格 if (object && object->method()) ... x = a || b || c;
* 简化复杂的逻辑 (方法: 反过来思考)
//集合覆盖 struct Range { int begin; int end; // For example, [0,5) overlaps with [3,8) bool OverlapsWith(Range other); }; // Bad: hard to check , with bug bool Range::OverlapsWith(Range other) { // Check if 'begin' or 'end' falls inside 'other'. return (begin >= other.begin && begin <= other.end) || (end >= other.begin && end <= other.end); } // Good: clear logic , easy to check , bug free bool Range::OverlapsWith(Range other) { if (other.end <= begin) return false; // They end before we begin if (other.begin >= end) return false; // They begin after we end return true; // Only possibility left: they overlap }
* 将大的表达式分解
方法: 重复的较长的变量或语句重新定义变量表示
* 另一种新颖方法简化表达式(善用C/C++宏,不可滥用宏)
// Bad : too many, hard to check void AddStats(const Stats& add_from, Stats* add_to) { add_to->set_total_memory(add_from.total_memory() + add_to->total_memory()); add_to->set_free_memory(add_from.free_memory() + add_to->free_memory()); add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory()); add_to->set_status_string(add_from.status_string() + add_to->status_string()); add_to->set_num_processes(add_from.num_processes() + add_to->num_processes()); ... } // Good : easy to check/read void AddStats(const Stats& add_from, Stats* add_to) { #define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field()) ADD_FIELD(total_memory); ADD_FIELD(free_memory); ADD_FIELD(swap_memory); ADD_FIELD(status_string); ADD_FIELD(num_processes); ... #undef ADD_FIELD }
如何善用变量提高代码的可读性?(chap 9)
* 变量带来的问题
1. 变量越多,越难跟踪它们
2. 变量的作用域越大, 需要跟踪越久
3. 变量的值改变越多, 越难跟踪他的当前值
* 减少变量(与之前的explaining/summary变量不矛盾,这里减少不必要的变量)
* 省去无用的临时变量
// Bad now = datetime.datetime.now() root_message.last_view_time = now // Good root_message.last_view_time = datetime.datetime.now()
理由:
1. now的引入并没有分解一个复杂的表达式
2. datetime.datetime.now()本身已经很清楚
3. now只用了一次, 并没有解决冗余代码
key: 这种类型的变量("leftovers"),往往保存一个临时结果,可以通过立即处理临时结果来省掉它们.
(complete the task as quickly as possible)
// Bad var remove_one = function (array, value_to_remove) { var index_to_remove = null; for (var i = 0; i < array.length; i += 1) { if (array[i] === value_to_remove) { index_to_remove = i; break; } } if (index_to_remove !== null) { array.splice(index_to_remove, 1); } }; // Good var remove_one = function (array, value_to_remove) { for (var i = 0; i < array.length; i += 1) { if (array[i] === value_to_remove) { array.splice(i, 1); // process immediately return; } } };
* 省去流程控制变量(Control flow variable)
// Bad : 不必要的变量 boolean done = false; while (/* condition */ && !done) { ... } if (...) { done = true; continue; } // Good : 这类流程控制变量都可以通过if-break/continue来做到 while (/* condition */) { ... if (...) { break; } }
* 缩短变量的作用域
key: 减少变量的可见范围,越少越好.
原因: 通过减少变量的可见范围,读者阅读代码时需要记住的变量就越少.
* C++中的if-语句的作用域
// Bad PaymentInfo* info = database.ReadPaymentInfo(); if (info) { cout << "User paid: " << info->amount() << endl; } // Many more lines of code below ... // Good : 用户看完之后就不需要关心info了 if (PaymentInfo* info = database.ReadPaymentInfo()) { cout << "User paid: " << info->amount() << endl; }
* 在js中使用"私有"变量
* 在js中使用var来定义局部变量
* 将声明下移(移到使用变量的附近)
// Bad def ViewFilteredReplies(original_id): filtered_replies = [] root_message = Messages.objects.get(original_id) all_replies = Messages.objects.select(root_id=original_id) root_message.view_count += 1 root_message.last_view_time = datetime.datetime.now() root_message.save() for reply in all_replies: if reply.spam_votes <= MAX_SPAM_VOTES: filtered_replies.append(reply) return filtered_replies // Good : 变量的声明以及使用都聚集在一块了 def ViewFilteredReplies(original_id): root_message = Messages.objects.get(original_id) root_message.view_count += 1 root_message.last_view_time = datetime.datetime.now() root_message.save() all_replies = Messages.objects.select(root_id=original_id) filtered_replies = [] for reply in all_replies: if reply.spam_votes <= MAX_SPAM_VOTES: filtered_replies.append(reply) return filtered_replies
* 倾向于一次写入的变量(常量,Write-once variable)
C++中的const, java中的final. 常量不容易带来问题
static const int NUM_THREADS = 10;