Berlinix书评

据说《重构》是与《设计模式》齐名的设计书。《设计模式》偏重如何在代码之初,就搭建一个漂亮的模型、清晰的结构;而《重构》则假设于你身处一个乌烟瘴气的代码泥沼里,并让自己活下来,且活的舒服点。试想,我们有多少人能在一张白纸上,从头架构一个新世界呢?我们更多的是在一个旧世界中,呼吸着肮脏的空气,行走在拥堵不堪的道路,常年堵塞的下水道散发着阵阵恶臭,除了盗贼,喧嚣的兵士还出没于人群,肆无忌惮。这才是程序员身处的真实世界阿!

在学生时代,我就曾潦草地翻阅过此书。如果存在时光隧道,让我穿梭回那个年月,我一定装扮为图书管理员对自己说“咳、放下它,那本(指向《C语言程序设计》)、那本(UNIX环境/网络编程)、那本(某本算法书吗?),甚至那本(《演员的自我修养》)都更适合你。”想想也对,生活在真空中的学生,又怎么能理解佩戴防毒面具的重要性呢?

直到有一天,你为了避免流落街头而在一家软件公司委屈就职时,你的Boss会扔给你一份厚重感十足、沉甸甸的代码仓库,让你维护。如果你有好莱坞导演的潜质,你一定能在浏览前辈们的光荣战绩时(是的,他们唯一的遗产就是这份百死不僵的代码),闪现过他们丰富多彩的一天(或一生,如果你把他们工作中的每一天叠加起来):一会儿是愁眉苦脸,一会儿是心不在焉;一面拆了西墙补东墙,一面又和了烂泥扶不上墙。

作为一名血气方刚、初出江湖的年轻人,你当然看不惯这样的作风。你义不容辞地向老板请示,愿用一年半载的时间,重新打造一份干净、整洁、接口稳定、功能一致的旗舰级代码。

本书的缺点是交叉索引过多,以致于显得作者是如此喋喋不休。像照顾刚学会行走的孩童的父母一般,不放心任何一点的磕绊;又像是叮嘱第一次出远门的少年,“临行密密缝,意恐迟迟归”。

笔记

9.2 合并条件语句

如果存在一连串检查:检查条件各不相同,最终行为却一致。这种情况下,应该将多个条件语句提炼成一个独立函数,把“做什么”的语句换成“为什么这么做”。

[反例] 检查订单状态:

double calc_shipping_fee()
{
    if(items_count == 0) return 0;
    if(all_virtual_items == true) return 0;
    if(in_the_same_city == true) return 0;
    /* calc */
}

[重构] 提炼多个条件到一个函数(有点卫句的味道):

bool is_freight_free()
{
    if( items_count == 0 or 
        all_virtual_items == true or
        in_the_same_city == true) {
        return true;
    }
    return false;
}

double calc_shipping_fee()
{
    if(is_freight_free()) return 0;
    /* calc */
}

9.5 嵌套条件语句

if/else的内涵有2种:

  1. 所有分支都是正常行为。理应用if/else。
  2. 只有一条分支是正常行为,其余都是非正常的。如果某个条件很罕见,就应该单独检查该条件,并在条件为真时,立即从函数中返回。而这样的单独检查称为Guard clauses(卫句)。

本条的精髓是:给重要的分支特别的重视。而卫句的作用则是告诉读者:这种情况很少发生,如果它真的发生了,请做一些必要的清理工作,然后退出。

在这里,Martin还明确反对了“函数单一出口”的做法,指出单一出口的信条往往是形成复杂条件语句的根源。如果单一出口使这个函数更清晰易读,那么就使用单一出口,否则就不必这么做。

[反例] 嵌套的条件逻辑(处理死亡员工、驻外员工、退休员工、正常员工的薪资):

double get_amount()
{
    double res;

    if(_is_dead) res = get_dead_amount();
    else {
        if(_is_separated) res = get_separated_amount();
        else {
            if(_is_retired) res = get_retired_amount();
            else res = get_normal_amount();
        }
    }

    return res;
}

[重构] 使用卫句:

double get_amount()
{
    if(_is_dead) return get_dead_amount();
    if(_is_separated) return get_separated_amount();
    if(_is_retired) return get_retired_amount();
    return get_normal_amount();
}

分享

0