整洁代码
- 让读的过程便轻松,即使编写过程困难
有意义的命名
名副其实
==命名必须有意义== - 模糊度:上下文在代码中未被明确体现的程度
避免误导
- 避免留下掩藏代码本意的错误线索,避免使用与本意相悖的词
- 提防使用不同之处较小的名称
做有意义的区分
通过区分提供不同的信息
使用读的出来的名称
- 方便口头表述
使用可搜索的名称
- 虽然长但方便检索和辨别
避免使用编码
避免思维映射
- 明确是王道
类名
- 名词
方法名
- 动词
每个概念对应一个词
- 函数名称应该独一无二并且保持一致
别用双关语
使用解决方案领域名称
- 尽量使用术语、算法名、模式名、数学术语
使用源自所涉问题领域的名称
添加有意义的语境
- 单独出现的
state
与添加了语境后单独出现的addrstate
- 更好的方案:创建名为
Address
的类
不要添加没用的语境
函数
短小
- 每个函数都只说一件事
- 每个函数都依序把你带到下一个函数
- 避免过多的缩进和嵌套循环
只做一件事
- 函数应该做一件事,做好这件事
- 要判断函数是否只做了一件事,就看是否能再拆除一个函数
每个函数一个抽象层级
抽象层次 - ryelqy - 博客园 - 要确保函数只做一件事情,函数中的语句都要在同一抽象层级上 - 代码清单3-7
switch语句
- 确保每个switch都埋藏在较低的抽象层级,并且永不重复
- 利用多态来实现
使用描述性名称
- 描述函数所做的事
- 别害怕长名称
- 别害怕花时间取名字
- 描述性的名称能够理清关于模块的设计思路
- 命名方式保持一致
函数参数
- 最理想:零参数
- 不太期望信息通过参数输出(利用面向对象可以一定程度上避免)
一元函数的普遍形式
- 询问有关参数的问题
- 操作该参数
- 如果函数要对输入参数进行转换操作,转换结果应该体现在返回值
标识参数
- 丑陋不堪,破坏了函数单个抽象层级
二元函数
- 自然的组合或自然的排序
- 尽量利用一些机制将二元转换为一元
三元函数
参数对象
- 如果函数需要三个以上的参数,则说明这些参数需要封装成类
无副作用
- 可能导致古怪的时序耦合及顺序依赖
分隔指令与询问
- 函数要么做什么事,要么回答什么事
==使用异常替代返回错误码==
- 使用异常代替返回错误码,错误处理代码就能从主路径代码中分离
- 抽离try/catch代码块,,独立形成一个函数
- 使用异常替代错误码,新异常就可以直接从旧异常中派生出来
如何写出这样的函数
- 粗稿:冗长而复杂
- 打磨:
- 分解函数
- 修改名称
- 消除重复
- 保持测试通过
注释
- 注释的作用:弥补在用代码表达意图时遭遇的失败
- 把力气放在书写清楚代码上
- 不准确的注释比没有注释坏的多
注释不能美化糟糕的代码
- 写注释的动机之一:糟糕代码的存在
- ==把代码弄干净!!==
- ==花时间清洁糟糕的代码==
用代码来阐述
好注释
- 法律信息
- ==提供消息==的注释
- 注释某个抽象方法的返回值
- 尽量利用函数名称传达消息
- 对意图的解释
- 阐释:
- 把晦涩难懂的参数或返回值的意义翻译为某种刻度的形式
- 警示
- ==TODO注释==
- 程序员认为应该做而还没做的事
- 定期查看,删除不再需要的
- 放大
- 放大某种看来不合理之物的重要性
- 公共API中的Javadoc
坏注释
- 喃喃自语
- 如果决定写注释,就花时间写最好的注释
- 多余的注释
- 误导性注释
- 循规式注释
- 日志式注释
- 废话注释
- 废话
- 能用函数或变量时就别用注释
- 位置标记
- 括号后的注释
- 归属与署名
- 注释掉的代码
- HTML注释
- 非本地信息
- 如果一定要写注释,确保它描述了离他最近的代码
- 信息过多
- 不明显的关系
格式
垂直格式
概念间垂直方向上的区隔
- 每组代码行展示一条完整的思路
- 每个空白行都是一条线索,标识出新的独立概念
垂直方向上的靠近
- 靠近的代码行暗示了他们之间的紧密关系
垂直距离
- 关系密切的概念应该互相靠近
- 变量声明:变量声明应该尽可能靠近其使用的位置
- 循环中的控制变量应该总是再循环语句中声明
- 实体变量应该在类的顶部声明
- 若某个函数调用了另外一个,就应该把他们放到一起
- 调用者应该尽可能放在被调用者之上
- 概念相关的代码应该放到一起
- 相关性越强,彼此之间的距离就应该越短
- 即使没有互相调用,也应该放在一起
横向格式
- 在赋值操作符周围加上空格字符,达到强调的目的
- 不再函数名和左圆括号之间添加空格,表明函数与其参数密切相关
- 空格字符的另一种用法:强调其前面的运算符
- 乘法因子之间没有空格,表明高优先级
对象和数据结构
数据抽象
- 以最好的方式呈现某个对象包含的数据
数据、对象的反对称性
- 过程式代码
- 在不改动既有数据结构的前提下添加新函数
- 难以添加新的数据结构,因为必须修改所有函数
- 在不改动既有数据结构的前提下添加新函数
- 面向对象式代码
- 在不改动既有函数的前提下添加新类
- 难以添加新函数,因为必须修改类
- 在不改动既有函数的前提下添加新类
得墨忒耳律
- 模块不应了解它所操作对象内部的情形
- 类C的方法f只应该调用以下对象的方法
- C
- 由f创建的对象
- 作为参数传递给f的对象
- 由C的实体变量持有的对象
数据传送对象
- 只有公共变量、没有函数的类
- 数据库通信、
错误处理
- 错误处理非常重要,但如果它搞乱了代码逻辑,就是错误的做法
使用异常而非返回码
- 遇到错误时,最好抛出一个异常
先写Try-Catch-Finally语句
- try是事务
- catch将程序维持在一种持续的状态
使用不可控异常
- 可控异常的代价:
- 违反开闭原则
- 如果异常可控,函数签名就要添加throw子句
给出异常发生的环境说明
- 抛出的每一个异常,都应当提供足够的环境说明,一边判断错误的来源和处所
- 创建信息充分的错误消息,并且和异常一起传递出去
- 传递足够的信息给catch块,并且记录下来
依调用者需要定义异常类
通过打包调用API、确保它返回通用异常类型->简化代码
别返回null值
- 如果打算在方法中返回null值,不如抛出异常,或是返回特例对象
- 如果在第三方API中可能返回null值的方法,可以考虑用新方法打包这个方法
别传递null值
除非API要求你向他传递null值,否则就要尽可能避免传递null值