• 断舍离 ——《代码整洁之道》读书笔记


    注1:只看了书的前十章
    注2:原书使用的语言为 Java,我改成了 JavaScript

    第一章 为什么要整洁代码


    1、代码永不消失

    代码就是衔接人脑理解需求的含糊性机器指令的精确性的桥梁。哪怕未来会有对现在高级编程语言的再一次抽象——但这个抽象规范自身仍旧是代码。

    所以既然代码会一直存在下去,且自己都干了程序员这一行了,就好好的对待它吧。

    2、读远比写多

    当你录下你平时的编码的过程,回放时,你会发现读代码所花的时间远比写的多,甚至超过 10:1。所以整洁的代码会增加可读性

    3、对抗拖延症——稍后等于永不

    糟糕的代码会导致项目的难以维护。而当你正在写糟糕的代码的时候,心里却圣洁的想:“有朝一日再回来整理”。但现实是残酷的,正如勒布朗(LeBlanc)法则:稍后等于永不(Later equals never)

    4、精益求精

    写代码就跟写文章一样,先自由发挥,再细节打磨。追求完美

    大师级程序员把系统当作故事来讲,而不是当作程序来写。

    第二章 命名


    1、名副其实

    (×)图表:mapTable

    (√)图表:chart

    推荐一个根据中文意思帮你起英文变量名的网址:http://unbug.github.io/codelf/

    2、避免误导

    (1)不要用专有名词

    const var = 0;

    (2)避免细微之处有不同

    XYZControllerForEfficientKeepingOfStrings

    XYZControllerForEfficientHoldingOfStrings

    (3)避免废话

    如果是一个数组类型,就没必要叫 ProductArray

    如果返回值就是数据,就没必要叫 ProductData

    (4)便于搜索

    (×)

    if ( team.length === 3 )
    

    (√)

    const MAX_NUM_OF_TEAM = 3 ;
    ……
    if ( team.length === MAX_NUM_OF_TEAM )
    

    MAX_NUM_OF_TEAM 比 3 好检索

    早期流行一种匈牙利语标记法

    如 arru8NumberList 的前缀 "arru8" 表示变量是一个无符号8位整型数组;

    3、避免思维映射

    类名、参数名用名词:

    member
    leader

    方法名用动词:

    getTeam

    根据 Javabean 的标准,方法名可以加上 get set is 前缀

    4、每个概念用一个词

    get fetch 选一个,不要混着用

    5、添加有意义的语境

    如果你要记录 member 的详细住址,会设置了如下几个变量:

    (×)

    addrStreet addrHouseNumber addrCity addrState
    

    (√)

    new Address {
        street,
        houseNumber,
        city,
        state
    }
    

    第三章 函数


    尽量短小

    函数的缩进层级尽量控制在 1-3 层

    例如要依次读取 A、B、C 三个文件:

    (×)

    function test() {
        readFileA(function (err, data) {
            // todo
            readFileB(function (err, data) {
                // todo
                readFileC(function (err, data) {
                    // todo
                    //……
                });
            });
        });
    }
    

    (√)

    function test() {
        try {
            let re_a = await readFileA();
            let re_b = await readFileB();
            let re_c = await readFileC(); 
        } catch (err) { 
        }
    }
    

    只做一件事

    判断标准:是否可以再拆出一个函数

    (×)

    function test() {
        //......
        Session.save();
        //......
    }
    

    (√)

    function test() {
        //......
        saveSession();
        //......
    }
    
    function saveSession(){ 
        Session.save();
    }
    

    每个函数即是一个抽象层,如上面的例子,Session 跟 test 函数不是一个抽象层,所以抽离出来。

    函数名代替注释

    长且具有描述性的名字比描述性的长注释好

    (×)

    //取出所有满员的团
    getSpecialGroup()
    

    (√)

    getGroupOfFullMember()
    

    参数顺序太多记不住怎么办?

    方法一:体现在函数名上

    (×)assertEqual(expected, actual)

    (√)assertExpectedEqualsActual(expected, actual)

    现代 IDE 已经具有鼠标移在调用函数名上可以浮窗显示形参列表了。

    方法二:让参数可以打乱传
    function getMember({isNew = false,isActivate = false}){
        console.log("isNew:"+isNew,", isActivate:"+isActivate)
    }
    
    getMember({isNew:true,isActivate:false}) //isNew:true , isActivate:false
    //不会因为传参的顺序记错而出错
    getMember({isActivate:false,isNew:true}) //isNew:true , isActivate:false
    
    //也支持默认参数
    getMember({})   //isNew:false , isActivate:false
    

    这里用到了 ES6 的新特性:解构赋值

    let obj = {a:1,b:2};  
    let {a,b} = obj;	// a = 1, b = 2
    
    方法三:减少参数

    最理想的参数数量 < 3,最好不用输入参数。

    A、布尔值参数 一拆二

    (×)getMember(isNew)

    (√) getNewMember() getOldMember()

    这里的前提是获取新、老会员的方法体代码不一样,不然还是共用在一个方法通过布尔值比较好。

    B、二元函数变一元

    function addSuffix(origin,suffix){
        return origin+"+"+suffix;
    } 
    console.log(addSuffix("hello","world"));
    
    console.log("hello".addSuffix("world"));
    

    异常代替返回错误码

    见我另一篇 《 如何做好错误处理?(PHP篇)》 里面有体现

    抽离 try / catch 块

    见我另一篇 《 如何做好错误处理?(PHP篇)》 里面有体现

    避免重复

    否则得修改多处地方

    结构化编程

    (1)每个函数都应该有一个入口和一个出口

    关于(只能有一个 return 语句,在结尾处 / 尽快 return,即有多个 return 语句)的争论:

    《结构化编程》的建议:

    function test(is) {
        let result;
        if(is){
            result = true;
        }else{
            result = false;
        } 
        return result;
    }
    

    《重构》的建议:

    function test(is) {
        if(is){
            return true;
        }else{
            return false;
        } 
    }
    

    这两者写法现在仍有争议,详细的讨论可以点击:https://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement

    (2)循环中尽量避免有 break 或 continue ,而且决不能出现 goto 语句。

    第四章 注释


    尽量用代码表达,而不是用注释

    就像上文提到的用详尽的函数名代替注释,或者:

    //代码过于追求简洁而导致这里要加详细的注释
    if( smodule.getDependSubsystems().contains(subSysMod.getSubSytem()) )
    
    //还不如这里做拆分,取易懂的变量名方便理解,就可以不用加注释或者少加
    ArrayList moduleDependees = smodule.getDependSubsystems();
    String ourSubSystem = subSysMod.getSubSystem();
    if( moduleDependees.contains(ourSubSystem) )
    

    原因是:注释存在的越久,随着代码的不断迭代,会离代码的距离越来越远,这个时候好的做法是同时维护代码 + 注释,而注释越多越复杂,维护的成本自然就上升了。

    注释不能美化糟糕的代码,尽量去优化代码

    好注释

    (1)法律信息、许可证、版权、著作权、外链文档的 url

    (2)对意图的解释

    (3)警示

    (4)TODO 注释

    坏注释

    (1)只有自己看得懂

    (2)多余的注释

    a、不能提供比代码更多的信息

    b、读的时间比直接看代码还久

    (3)歧义性

    (4)循规蹈矩的注释

    例如用第三方工具生成的注释,很多都是累赘的废话

    (5)日志式、署名式注释

    (×)

    //  write by colin
    //  fix #201 bug
    

    (√)
    交给 git 记录

    (6)慎用位置标记

    // **********************
    

    及时清理不需要的注释

    (1)越堆越多

    (2)导致以后因看不懂而不敢删

    第五章 格式


    向报纸学习

    (1)从上往下读,从左往右读

    (2)源文件在最顶端给出高层次的概念和算法,细节往下逐次展开,直到最底层的函数和细节。

    垂直格式

    (1)善用空白行:人会更容易将目光聚焦到空白行之后的那一行

    (2)函数:调用者放在被调用者上面

    横向格式

    (1)缩进

    (2)IDE 中不会出现横向滚动条

    第六章 对象和数据结构


    数据结构对象的区别

    数据结构暴露其数据,没有提供有意义的函数。

    对象把数据隐藏在抽象之后,暴露操作数据的函数。

    //数据结构
    function Point(x,y){
        this.x = x;
        this.y = y;
    }  
    
    //对象
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      getX() {
        return this.x;
      }
      setX(x) {
        this.x = x;
      }
    }
    

    ES6暂不提供私有变量(虽然有提案),但可以通过其他办法变相实现:https://juejin.im/entry/572c0b2d2e958a00667a081d

    第七章 错误处理


    使用异常而非返回码

    (上文有述)

    定义异常类

    如引用了一个第三方库,它会 throw 自己的几种异常值,但我们可以定义一个异常类,封装好它的几种异常值为一种专门异常,然后再二次 throw 交给上层捕获。

    别返回 null 值

    null 值会造成很多不必要的 if 判断

    function getCurrentMember(){  
        let a = DB.getCurrentMember(); 
        if (a){
            return a;
        }else{
            return null;
        }
    }
    

    方法一:抛异常

    function getCurrentMember(){ 
        let a = DB.getCurrentMember(); 
        if (a){
            return a;
        }else{
            throw Error("no member")
        } 
    }
    

    方法二:返回特例值

    
    function getCurrentMember(){ 
        let a = DB.getCurrentMember(); 
        if (a){
            return a;
        }else{
            return {};
            //return {name : "default"};
        } 
    }
    
  • 相关阅读:
    SQLite无法使用drop column删除表字段解决办法
    Selenium之自动发送163邮件(转自https://www.cnblogs.com/zhang-da/p/12230415.html)
    frp 内网穿透
    smart.supor.com
    python colorama模块
    nohup和&后台运行,进程查看及终止
    Beef搭建并通过Nginx绑定域名
    How to Install Ruby on CentOS/RHEL 7/6
    Linux环境下安装python3
    启用IIS Express SSL(Https)的注意事项
  • 原文地址:https://www.cnblogs.com/xjnotxj/p/9690028.html
Copyright © 2020-2023  润新知