• 读书笔记之《The Art of Readable Code》Part 2


    如何写好流程控制语句(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;







     




  • 相关阅读:
    Uva10305(dfs)
    Uva572
    Uva122
    Uva679
    Uva136
    Uva489
    Uva133
    Uva1339
    Uva1588
    《世纪的哭泣》读后感 读书笔记
  • 原文地址:https://www.cnblogs.com/xianzhon/p/6291322.html
Copyright © 2020-2023  润新知