Robert 认为编程是一种技艺甚于科学的东西,要编写整洁代码,必须先写肮脏代码,然后再清理。
跟写作文先写草稿,再写第二稿,直至最终写出终稿,这是一个逐步改进的过程。
写出能工作的程序远远不够 ,要以整洁代码为目标,只有达到整洁代码后,才可转移到下一个任务上。
- 优秀的软件设计是,大都关乎分隔:创建合适的空间,放置不同种类的代码,对关注面的分隔让代码更易于理解和维护
- 代码能工作不够,能工作的代码经常会严重崩溃
- 解决之道:保持代码持续整洁和简单,永不让“腐坏”有机会开始
童子军军规
让营地比你来时更干净
代码离开时比来时更加整洁
味道与启发
注释
不恰当的信息:注释只应描述有关代码和设计的技术性信息
废弃的注释
冗余注释
糟糕的注释
注释掉的代码:如果看到,立马删除它,可以通过 git 追溯
环境
需要多步才能实现的构建:构建系统应该是单步的小操作
需要多步才能做到的测试:单个指令就可以运行全部UT
函数
过多的参数:没有最好,三个以上的参数应坚决避免
输出参数:输出参数违反直觉,参数一般用于输入而非输出;如果函数非要修改什么东西的状态,就修改它所在对象的状态好了
标识参数:布尔值参数就意味着函数做了不止一件事,令人迷惑,应该删掉
死函数:永不被调用的函数应该删除
一般性问题
一个源文件中存在多种语言:理想源文件有且只包括一种语言
明显的行为未被实现:在实现时,把自己当做用户,看方法是否如自己所期待
不正确的边界行为:别依赖直觉,追索每种边界条件,并编写测试
忽视安全
重复:尽可能找到并消除重复
在错误的抽象层级上的代码:
创建分离较高层级一般性概念与较低层级细节概念的抽象模型很重要
只与细节实现有关的常量、变量或工具函数不应该在基类中出现,基类应对这些一无所知
基类依赖派生类,通常来说,基类应对派生类一无所知
信息过多:设计良好的模块有着非常小的接口
死代码:就是不执行的代码,看到就删掉吧
垂直分隔:变量应该在首次被使用的位置上声明,函数在首次被使用的位置下面定义
前后不一致:从一而终,小心选择约定,一旦选中就小心持续遵循
混淆视听:包含没实现的默认构造器,从不调用的函数,没有用到的变量,没有信息量的注释
人为耦合:不互相依赖的东西不该耦合
特性依恋:类的方法只应对其所属类中的变量和函数感兴趣,不该垂直其他类中的变量和函数,当方法通过某个其他对象的访问器和修改器来操作该对象内部数据时,它就依恋该对象所属类的范围
选择算子参数:使用多个函数,通常优于向单个函数传递某些代码来选择函数行为
晦涩的意图:代码要尽可能具有表达力
位置错误的权责:软件开发做出的最重要的决定之一就是在哪里放代码
不恰当的静态方法:通常倾向于选用非静态方法,如果的确需要静态函数,确保没机会打算让它有多态行为
使用解释性变量:解释性变量多比少好
函数名称应该表达其行为
理解算法:确认自己理解了算法是如何工作的,通过全部测试还不够好,必须知道解决方案是正确的
- 获得这种知识和理解的最好途径往往是重构函数,得到某种整洁而足具表达力、清楚呈示如何工作的东西
把逻辑依赖改为物理依赖
用多态替代 If/Else 或 Switch/Case
遵循标准约定
用命名常量替代魔术数
准确:代码中的含糊和不准确,要么是意见不同的结果,要么源于懒惰,无论原因是什么,都要消除
结构甚于约定:坚守结构甚于约定的设计原则,命名约定很好,但是次于强制性的结构
封装条件:应该把解释了条件意图的函数抽离出来
避免否定性条件:否定式要比肯定式难明白一些,尽可能将条件表示为肯定形式
函数只该做一件事
掩蔽时序耦合:通过创建顺序队列,每个函数产生下个函数所需的结果
别随意:不作为类工具的公共类,不应放到其他类里面
封装边界条件
函数应该只在一个抽象层级上
在较高层级放置可配置数据
避免传递浏览:Law of Demeter
不要继承常量
为较大作用范围选用较长名称
名称应该说明副作用