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

如何重新组织代码提高可读性? (函数层面, part 3)
1. 抽取与主要问题无关的代码
2. 重新组织代码使得一次只做一件事
3. 首先描述功能,然后再实现功能,这样更清楚明了

如何抽出问题无关的子问题? (chap 10)
0. 无关问题的思考
 - 看到一个函数或一个代码块, 问自己, "这段代码的高层作用是什么(high-level gloal)"
 - 对于每一行代码, 思考"它是直接解决这个目标吗",还是"解决一个子问题来达到目标的解决"
 - 如果是解决子问题,并且代码的行数也足够多,那么就可以抽取出一个独立的函数

// Return which element of 'array' is closest to the given latitude/longitude.
// Models the Earth as a perfect sphere.
var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        // Convert both points to radians.
        var lat_rad = radians(lat);
        var lng_rad = radians(lng);
        var lat2_rad = radians(array[i].latitude);
        var lng2_rad = radians(array[i].longitude);
        // Use the "Spherical Law of Cosines" formula.
        var dist = Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad) +
                Math.cos(lat_rad) * Math.cos(lat2_rad) *
                Math.cos(lng2_rad - lng_rad));
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
};


// Good : clear, easy to test spherical_distance in isolation
var spherical_distance = function (lat1, lng1, lat2, lng2) {
    var lat1_rad = radians(lat1);
    var lng1_rad = radians(lng1);
    var lat2_rad = radians(lat2);
    var lng2_rad = radians(lng2);
    // Use the "Spherical Law of Cosines" formula.
    return Math.acos(Math.sin(lat1_rad) * Math.sin(lat2_rad) +
            Math.cos(lat1_rad) * Math.cos(lat2_rad) *
            Math.cos(lng2_rad - lng1_rad));
};

var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitude);
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
};

 * 纯粹的Utility代码 (比如操作字符串,hash表,读写文件)
这类代码最好封装到一个独立的Util类或者函数,(ReadFileToString())

* 其他通用的代码

ajax_post({
    url: 'http://example.com/submit',
    data: data,
    on_success: function (response_data) {
        var str = "{\n";
        for (var key in response_data) {
            str += " " + key + " = " + response_data[key] + "\n";
        }
        alert(str + "}");
    });
}
// Continue handling 'response_data' ...

// Good : 函数名更容易定位,更容易维护
var format_pretty = function (obj) {
    var str = "{\n";
    for (var key in obj) {
        str += " " + key + " = " + obj[key] + "\n";
    }
    return str + "}";
};


// Better : 功能更强大
var format_pretty = function (obj, indent) {
    // Handle null, undefined, strings, and non-objects.
    if (obj === null) return "null";
    if (obj === undefined) return "undefined";
    if (typeof obj === "string") return '"' + obj + '"';
    if (typeof obj !== "object") return String(obj);

    if (indent === undefined) indent = "";

    // Handle (non-null) objects.
    var str = "{\n";
    for (var key in obj) {
        str += indent + " " + key + " = ";
        str += format_pretty(obj[key], indent + "
                ") + "\n";
    }
    return str + indent + "}";
};

 * 创造更多通用功能的代码
像之前的ReadFileToString(),format_pretty()都可以跨工程使用。

* 工程特有的功能 (即使如此,依然可以考虑创建在工程内部使用的util类/函数)
* 简化现有的接口 (用到某个第三方的库,它的接口不好用,可以考虑对它封装一下)
  - Simplifying an existing interface
  - Reshaping an interface to your needs
  - Don't take things too far (不要将一个函数实现细分成太多的小函数,要控制粒度)

一次只做一件事情(chap 11, one task at a time)
0. 一个代码片段只做一件事情 (空行风格的代码段)
示例1

// Bad : two things doing together (parseValue, computeScore)
var vote_changed = function (old_vote, new_vote) {
    var score = get_score();
    if (new_vote !== old_vote) {
        if (new_vote === 'Up') {
            score += (old_vote === 'Down' ? 2 : 1);
        } else if (new_vote === 'Down') {
            score -= (old_vote === 'Up' ? 2 : 1);
        } else if (new_vote === '') {
            score += (old_vote === 'Up' ? -1 : 1);
        }
    }

    set_score(score);
};


// Good : clear to understand
var vote_value = function (vote) {
    if (vote === 'Up') {
        return +1;
    }
    if (vote === 'Down') {
        return -1;
    }
    return 0;
};


var vote_changed = function (old_vote, new_vote) {
    var score = get_score();

    score -= vote_value(old_vote); // remove the old vote
    score += vote_value(new_vote); // add the new vote

    set_score(score);
};

 示例2

// Bad : 不方便扩展

var place = location_info["LocalityName"]; // e.g. "Santa Monica"
if (!place) {
    place = location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
}
if (!place) {
    place = location_info["AdministrativeAreaName"]; // e.g. "California"
}
if (!place) {
    place = "Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
    place += ", " + location_info["CountryName"]; // e.g. "USA"
} else {
    place += ", Planet Earth";
}
return place;


// Good

var town = location_info["LocalityName"]; // e.g.  "Santa Monica"
var city = location_info["SubAdministrativeAreaName"]; // e.g.  "Los Angeles"
var state = location_info["AdministrativeAreaName"]; // e.g.  "CA"
var country = location_info["CountryName"]; // e.g.  "USA"

// Start with the default, and keep overwriting with the most specific value.
var second_half = "Planet Earth";
if (country) {
    second_half = country;
}
if (state && country === "USA") {
    second_half = state;
}

var first_half = "Middle-of-Nowhere";
if (state && country !== "USA") {
    first_half = state;
}
if (city) {
    first_half = city;
}
if (town) {
    first_half = town;
}

return first_half + ", " + second_half;


// Better : 利用js语法的a||b||c的特性

var first_half, second_half;
if (country === "USA") {
    first_half = town || city || "Middle-of-Nowhere";
    second_half = state || "USA";
} else {
    first_half = town || city || state || "Middle-of-Nowhere";
    second_half = country || "Planet Earth";
}

return first_half + ", " + second_half;

 









posted @ 2017-01-18 22:17  编程爱好者-java  阅读(194)  评论(0编辑  收藏  举报