《代码简洁之道》读书笔记

124 阅读11分钟

重点章节

  第二章:有意义的命名

  建议

  • 不要写A,B,C变量名

  • 避免误导的单词,给出具有实际意义的单词

  • 有意义的区分

    • 反例:nameString
  • 不要用拼音

  • 方便搜索的命名

  • 避免编码问题

    • utf_开头等

  应用

  • 一个概念一个单词,或者使用相近的单词,比如serialDate

  • 少用双关的句子

    • add可以表示多种可能的含义,可以是添加,可以是追加.....
  • 一个有意义的语境

    • 方法名和内容贴合
  • 不需要无用的方法和内容,方法应该出现在该出现的位置

  第三章:函数

    重点

  • 时刻保持参数的数量控制
  • 函数要返回期望的内容,运行期望的行为
  • 避免副作用的方法
  • 取个好名是好方法的关键步骤

    简介

  • 介绍如何写出更加简单并且令人夸赞的好方法
  • 函数式封装的第一个步骤,时刻练习如何写出好的函数

    建议

  • switch语句结合抽象工厂,避免少用很多switch分支

  • 函数名词长并且见文知意,可以很好的把握意思,如果短小并且易懂就是好名字

  • 函数参数尽量控制在2个以内。函数的结果要尽量放到返回值,比如 obj transform(obj)

  • 询问和命令要分开,不能在if进行数据处理的同时返回信息

  • 函数不应该有副作用:比如函数不应该出现某些情况下的错误信息

  • 少使用嵌套复杂的代码

    • 方法内容越少越好
    • if内容一行最佳
    • 函数只做一件事情

    应用

  • 方法超过3个以上的时候,思考是否需要抽取到一个单独对象,当达到四个以上的时候,为每一个参数介绍作用(希望永远不要这样做)

  • 工具类建议学习框架的工具类,比如CollectionUtils,BeanUtils等

  • 不要返回NULL,容易造成空指针异常

  • 自顶向下读代码 #重点

    • 什么是自顶向下

      • 从一个T0的头部到另一个T0的头部
      • 容纳设置与分析步骤,先容纳设置步骤
      • 纳入测试页面内容,纳入分析步骤
    • 简单理解

      • 设置执行步骤,提取主干方法
      • 设置步骤(伪代码),再实现分支
      • 用搜索代替上下关系
    1. 如何判断方法只做一件事

    • 是否能再抽取一个方法
    • 是否是对诠释的重新实现

    正例

  • checkEmptyAndNull()
  • doSomething(Object obj)

    反例

  • return obj == null? null:new Object
  • method(param1, param2, param3, param4, param5)

  第四章:写出一个好注释

    重点

  • 写出好代码才是关键,先写出好简洁代码,再考虑写好注释
  • 最好的状态是代码本身就能诠释注释
  • 努力写出简单易懂的好注释
  • 好注释不仅可以让阅读者理解,还可以让阅读者学到新知识

    简介

  • 写一个注释容易,但是写好一个注释不容易
  • 程序员因为各种“借口”懒得写注释
  • 如果你的代码足够具备表达力,就不需要注释,否则请加上你的注释

    建议

  • 阐述性注释保证注释的正确性,谨慎编写
  • 警告性注释可以是说明禁止那样做,但是更好的方式是警告为什么要那样做
  • 如果代码很难被解释,需要依赖注释解答,那代码就不是好代码,请确保编写好理解的代码,那么就不需要注释
  • 注释不要喃喃而语,不要写意义不明的注释或者对当时自己有意义的注释
  • 不要过于详细的注释,比如为每一个变量介绍他是干嘛用的
  • 不要编写HTML标签注释,特别是复杂的HTML标签
  • 定期删除TODO注释

    应用

    1. 定期回顾自己写过的代码,对于注释进行回顾或者补充
    1. 注释要“有始有终”,要么全写,要么全不写
    1. 时常思考注释是否真的有必要,谨慎对待每一行注释
    1. 注释按照某种原则,可以让阅读者更好的理解
    1. 勇敢的删除注释掉的代码,版本控制工具会帮忙“找回”

    正例

  • 外国人的汇率换算和国人思路相反,比如人民币兑换美元,应该查找美元兑换人民币的汇率
  • SimpleDateFormat 线程是不安全的,不能使用静态或者单例构建,这里必须每次实例化
  • 5!代表 12345.....

    反例

  • 一行代码一行注释
  • 为了赶工一行注释也不写
  • 注释罗里吧嗦或者做一些奇怪的标记
  • 把阅读者当初刚入行的新手,把注释写满

  第五章:格式

  建议

  • 方法不应该超过120行,80行最佳
  • 一个文件代码不要超过500行
  • 遵循编程习惯,把变量声明在正确的位置
  • 代码宽度在120个字符左右(不要依赖带鱼屏)
  • 向报纸学习,学习如何简化头部和进行良好的排版

  第七章:异常处理

    重点

  • 异常处理的一些技巧

    • 用单独的方法进行try/catch
    • 从大的异常到细化异常
  • 不要返回NULL值

  • 异常需要作为单一职责看待,而不是和方法捆绑

    简介

  • 异常处理是一种权责,不要和业务混淆
  • 异常处理的基本手段和一些原则介绍

    建议

  • 用异常代替繁多的if else判断,是一种好习惯
  • 异常就是一件事,所以异常最好单独为一个函数,同时catch的内容不应该做其他事情
  • 不要用try包裹一大段代码,同时异常处理应该尽可能细节化
  • 异常处理应该是单独的方法,可以使用内部方法抛出异常
  • 如果不知道如何处理异常,就需要及时抛出异常通知调用者可能存在异常

    应用

  • 异常最好在一个方法的开头就定义好
  • 将可能出现异常的部分和永远不会出现异常的部分分离

    正例

  • 异常用单独的方法进行封装
  • 可以处理的异常,进行日志记录和提示打印
  • 及时在方法声明有可能的异常信息

    反例

  • 几百行代码使用一个try/catch
  • 捕获异常,不进行处理,同时不做日志记录

  第九章:单元测试

    重点

  • 测试单元:一个测试一个断言,测试单元尽可能简短

  • FIRST原则

    • F:快速

      • 测试可以快速进行
    • I:独立

      • 测试之间相互独立
    • R:可重复

      • 测试在任何环境都可以通过
    • S:自足验证

      • 存在布尔值输出,可以验证自己的结果
    • T:及时

      • 测试要及时进行编写

    简介

  • 学习使用TDD测试驱动开发的形式

  • 单元测试是非常重要并且必要的

    • 测试的好处
    • 整洁的测试
  • TDD的三大定律

    • 编写不能通过的测试代码之前,不编写生产代码
    • 只编写刚好无法通过的单元测试
    • 只编写刚好足以通过测试的生产代码

  第十章:类

    重点

  • 少量的大类并不一定比大量小类好管理
  • 用尽可能少的类和方法完成目的
  • 类不应该有太多的权责和干扰

    简介

  • 关键内容在于类的权责拆分

    • 当失去内聚的时候就想办法拆分
    • 生活抽象:你是要一个装任何东西的百宝箱还是一个布满各种方格的工具箱

  第十七章:味道和启发 #重点

    重点

  • 总结全书的一些要点
  • 回顾全书的重点内容

    简介

  • 非常重要的一个章节,用一个章节概括了全书的一些重要内容
  • 可以从最后一章确定全书要阅读的部分

    注释

  • 不恰当的注释信息

    • 表达意义模糊不清
  • 糟糕的代码

    • 注释无法解释的代码
  • 冗余的注释

    • 对一段内容反复说明
  • 糟糕的注释

    • 画蛇添足
    • 花了时间但是没有思考
  • 注释掉的代码如何处理

    • 不知道是否需要删除的代码
    • 放心删除被注释的代码

    环境

  • 使用单步操作构建一整个系统
  • 评估多少步可以完成测试

    函数

  • 杜绝过多的参数
  • 注意输出参数 - 返回值

    一般性问题 #重点

  • 一个源文件不应该有很多种语言,比如JAVA doc的HTML代码

  • 函数应该事先期待的行为并且返回期待的结果

    • 最小惊异原则
  • 不正确的边界行为

  • 忽视安全机制

    • 序列化的SerialId问题
  • 尽可能的的消除重复,才能写出更好的代码 #重点

  • 使用抽象概念定义更加高级的抽象模型

    • 较低的概念在子类,较高级的概念在父类 #重点
  • 基类依赖派生类

    • 基类概念可以不依赖于低层级的派生类概念
  • 接口的信息应该尽可能的精简,否则将会有复杂的实现 #重点

    • 如果类存在过多的信息,应该思考如何分解
  • 死代码问题

    • 一般性永远不会执行的代码在现代可能永远不会排查
    • 预防逻辑上永远不会执行的代码
  • 垂直分隔

  • 前后保持一致

    • 最小惊异原则
  • 移除混淆视听的代码

  • 伪耦合:在合理位置放置合适内容

  • 特性依恋问题

    • 一个类的对象不应到依赖一个不相干的类,依赖应该保持在一个类内部
    • PS:个人不是很同意问题
  • 算子参数

    • 警惕因为选择值的不同导致不同结果的方法 (Boolean参数)
  • 代码具备强烈的表达含义

  • 正确的函数名与正确的位置

  • 确保静态方法没有多态的行为

  • 良好的解释性变量可以使程序易读

  • 良好的函数名应该见名知意

  • 确保对于自己做的事情有全方位考虑而不是正常的运行

  • 逻辑依赖改为物理依赖 #重点

    • 尽可能避免直接使用类内部常量,而是方法当中进行保护

    • 举例

      • page-size = 35 改为 geMaxSize()
  • 多态的选择分支

    • 单个switch原则

      • 支持一次类型判定同时case内容必须为多态
  • 使用变量代替魔法值,尽可能少用魔法值

  • 确保代码足够精确,在不同的情况下出现期望的行为

  • 在IF里面尽可能少用“双重否定” #重点

  • 函数永远只做一件事情 - 单一职责的原则

  • 传递浏览信息

    • A->B,B->C,A不能知道C的信息
  • 如果派生类多次用到同一个配置,应该将配置放到最高层

    JAVA部分

  • 避免过长导入清单,Java.util.* 问题
  • 用静态导入代替常量的继承
  • 用枚举替代PSF常量(public static final)

    名称

  • 描述性名称

  • 名称符合抽象概念

  • 无歧义名称

  • 标准命名法

    • 驼峰命名
    • 首字母大写
  • 名称具备副作用解释

    • exists
    • null

附录:并发编程补充

  影响线程运行的因素(性能)

    IO密集型

  • 套接字
  • 连接数据库
  • 内存交换

    处理器

  • 正则运算
  • 垃圾回收
  • 算法

  如何测试线程

  • 增加线程代码
  • 通过测试的代码不一定正确

  分析服务端代码权责

  • 套接字连接
  • 客户端处理
  • 线程策略
  • 服务器关闭

心得和总结

  • 边看边思考,边看边实践。实践最重要

  • 代码不是一步到位的,代码永远都没有最优解,根据自己的能力要不断摸索更优解。刻意练习

  • 不想看书的一些建议点

    • 单一职责

      • 代码是否真的只做了一件事,是否在自己骗自己
      • 验证是否只做一件事的万金油法则:加需求
      • 方法是否是“全包干”
    • 不要重复自己

      • "CV"编程会让水平停留在"CV"
      • 现代的编辑器可以减少很多思考
    • 什么是简洁

      • 没有注释,读代码就像在读业务逻辑
      • 没有过时的注释,也没有错误的注释,更没有没用的注释
      • 用最少的代码做最大的事情,代码复用度高
      • 没有重复,单一职责
    • 迭进

      • 学会拆分职责和剥离职责
      • 思考何为面向对象
      • 不断的改进,写出更简洁的代码
    • 批判精神

      • 敢于重构代码,对于自己编写的代码发出挑战
      • 接受批评,才会不断进步
      • 运行所有测试
      • 不可重复
      • 表达程序员意图
      • 尽可能减少类和方法
    • 对象与数据结构

      • 得墨忒耳定律

        • 模块不应该了解对象内部的情形
      • 对象和数据结构的二分定律

        • 过程式代码便于不改动既有结构上加入新函数。面向对象在不改动既有函数情况下增加新类
        • 过程式代码难以加入新的数据结构,面向对象代码难以加入新的函数