《可读代码的艺术》读书笔记2

本文章学习自英文版书籍《The Art of Readable Code》。

懂得注释什么

《可读代码的艺术》读书笔记2_第1张图片
  • 不要注释从代码就可以一眼看出来的内容
    注意“一眼”很重要,如果不是一眼,请注释
# remove everything after the second '*'
name = '*'.join(line.split('*')[:2])

上面虽然没有注释看一会也可以明白,但是加上注释就快了很多。

  • 不要为了注释而注释
// Find the Node in the given subtree, with the given name, using the given depth.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);
  • 不要搞一个破名,然后搞一堆注释,还是把名字想好先吧

  • 注释就像是创作心声

// Surprisingly, a binary tree was 40% faster than a hash table for this data.
// The cost of computing a hash was more than the left/right comparisons.
  • 代码的问题也要注释出来

  • 给常数注释

  • 换位思考:想象你是看代码的人

  • 避免含糊不清的代词
    it、this、

  • 举例子
    显然第二段代码的注释更清楚

// Remove the suffix/prefix of 'chars' from the input 'src'.
String Strip(String src, String chars) { ... }
// Example: Strip("abba/a/ba", "ab") returns "/a/"
String Strip(String src, String chars) { ... }
  • 注释要表达代码真真意图而不是代码字面意思(high-level,big picture)
void DisplayProducts(list products) {
products.sort(CompareProductByPrice);
  // Iterate through the list in reverse order
  for (list::reverse_iterator it = products.rbegin(); it != products.rend();++it)
    DisplayPrice(it->price);
...
}
// Display each price, from highest to lowest
for (list::reverse_iterator it = products.rbegin(); ... )

让控制流更加容易读

  • 左边变量,右边常量,第一个会更容易读一点
if (length >= 10)
if (10 <= length)
  • if/else,先判断哪种情况好呢?
    下面哪个好呢?
if (a == b) {
// Case One ...
} else {
// Case Two ...
}
or as:
if (a != b) {
// Case Two ...
} else {
// Case One ...
}

准则:
1、要正的,不要反的

if(debug)好于if(!debug)

2、先处理简单的,再处理难的,这样也可以让if和else两个语句块都在同一屏里面
3、先处理特殊的、感兴趣的

上面的原则可能会冲突,你要进行衡量。

  • 简单的判断可以使用三目运算符,但是复杂的还是用if else

  • 不要使用do while

  • 函数提早返回
    如果没有不是下面的return会感觉很不自然。

public boolean Contains(String str, String substr) {
if (str == null || substr == null) return false;
if (substr.equals("")) return true;}

有时可能会想要同一个退出点,以便可以执行一些收尾工作,一些语言提供了这样子的机制:


《可读代码的艺术》读书笔记2_第2张图片

C语言没有这样子的机制,可以重构函数或者用下goto

  • 减少嵌套
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();

如果是在一个循环里面嵌套用continue,原则同上面的解决方案

for (int i = 0; i < results.size(); i++) {
  if (results[i] != NULL) {
    non_null_count++;
    if (results[i]->name != "") {
      cout << "Considering candidate..." << endl;
      ...
    }
  }
}

解决方案:

for (int i = 0; i < results.size(); i++) {
if (results[i] == NULL) continue;
non_null_count++;
if (results[i]->name == "") continue;
cout << "Considering candidate..." << endl;
...
}

分解大块的表达式

  • 使用一些临时变量代表比较长的表达式
    下面第一段真的是辣眼睛!!!!
var 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("highighted");
  $("#thumbs_down" + message_num).removeClass("highlighted");
}
};

化简后,要遵循DRY——Don’t Repeat Yourself原则

var 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);
}
}

另外一个例子

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());
  ...
}

修改后:

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
}
  • 使用De Morgan’s公式来化简代码逻辑表达式
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 you have trouble remembering these laws, a simple summary is “Distribute the not and switch and / or .” (Or going the other way, you “factor out the not .”)

例子:

if your code is:
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
It can be rewritten to:
if (!file_exists || is_protected) Error("Sorry, could not read file.");
  • 不要滥用逻辑运算表达式
    下面这段看的时候要好好想一想
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

改成这段会比较好,虽然第一段很紧凑,但是可读性更重要

bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
  • 一个例子
    需求:判断两个区间是否重叠,下面图片中,A、B、C互相不重叠,D重叠与它们都重叠


    《可读代码的艺术》读书笔记2_第3张图片

数据结构设计如下:

struct Range {
  int begin;
  int end;
  // For example, [0,5) overlaps with [3,8)
  bool OverlapsWith(Range other);
};

第一种方案:虽然很简短,但是实际代码逻辑很复杂,并且有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);
}
《可读代码的艺术》读书笔记2_第4张图片

第二种方案:通常遇到上面的情况,我们会向,肯定会有一种更好的解决方式,但这需要创造力,其中一个技巧是使用反向。要求是重叠,那我们就看能不能用不重叠的方式解决,然后就:

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
}

你可能感兴趣的:(《可读代码的艺术》读书笔记2)