CleanCode学习笔记
2019-10-15 本文已影响0人
左心Chris
1 Intro
2 命名
确定统一,可读搜索,类名方法,专业语境
- 确定性 如计量对象和计量单位 daysSincesCreation fileAgeInDays elapsedTimeInDays
- 避免误导 accountList可能不是list,用accountGroup或则bunchOfAccounts代替;小心使用不同地方较小的名称;1和0和l和O区分
- 有意义的区分 如source和destination,有些如a the Data Info variable string就是废话
- 使用读的出来的名称
- 使用可搜索的名称
- 避免Encodings ? 如果有
1 匈牙利标记法 首字母体现类型不需要,因为当代语言有类型检查,而且可能造成误导
2 成员前缀 m_也不需要
3 接口和实现 接口ShapeFactory 实现是ShapeFactoryImp或者 CShapeFactory ? - 避免思维映射 明确,比如把r自己映射为小写url
- 类名 名词或者名词短语
- 方法名 动词或者动词短语;属性访问器,修改器,断言用get, set, is 前缀
? 重载构造器时,使用描述了参数的静态工厂的方法名 - 每个概念对应一个词 如controller, manager, driver或者fetch, receive, get
- 别用双关语 add,insert,append
- 使用解决方案领域名称或者源自所涉及的问题的名称
- 添加有意义的语境,不要没用的语境 需要良好命名的类,函数或者名称空间来添加语境
3 函数
确定单一,输入输出,查改分离,参数异常
- 短小
- 只做同一抽象层级的事
- 每个函数一个抽象层级
- switch语句 用多态把不同switch语句用同一个接口实现
- 使用描述性的名称 isTestable 和 inclueSetupAndTeardownPages, includeSetupPages
- 函数参数 调用的时候参数越少,顺序有可能判断错,如果太多封装为类
- 一元参数 问跟这个参数的问题,或者转化这个参数,还有事件有输入无输出
- 标识参数 如果传入标识参数,表明这个函数会做两件事,true做一件,false做一件
- 二元函数 顺序问题,可以这样:outputStream.writeField(name) 或者把outputStream写为类成员变量,避免writeField(outputStream, name),python还好
- 三元函数 封装为类
- 参数列表 可变参数列表在最后,函数可能是一元,二元或三元,超过可能有问题
- 无副作用 函数只干一件事,不要做函数名没有声明的事
- 输入输出 避免使用输出作为参数传入 ,如appendFooter(s) 而改为 report.appendFooter()
- 分割指令和询问 if (attributeExist()) {setAttribute()}
- 使用异常替换返回错误码
- try {} catch (Exception e) {logger.log(e.getMessage())}
- 抽离 try/catch 代码块 ?try {function()} catch (Exception e){ logError(e)} function() throws Exception {}
- 错误处理就是一件事 try在函数就是函数的第一个单词,catch/finally 后面没有内容
- 使用异常代替错误码 这样异常继承异常类不像改错误码类一样,更改就得重新构建部署
- 避免重复,结构化编程(一个入口一个出口,没有break,continue,goto),迭代开发
4 注释
代码是最好的注释
好注释和坏注释
- 因为代码一直在变化,但是注释不是一直在演进,所以代码是最好的注释
- 好注释
- 法律信息
- 信息的注释,意图的解释,阐释(翻译复杂的参数或者返回值),警示
3 TODO注释,放大重要性,公共api中的javadoc
- 坏注释
- 想好的注释再写
- 多余的注释(代码跟注释表示一种意思),尽量用函数和变量来表明名字
- 误导性注释,不明确的注释,过多的注释
- 循规注释(每个函数都有javadoc)
- 日志式注释 (已经有版本控制系统不需要了)
- 位置标记,括号后面的注释,归属与署名
- 注释掉的代码,HTML注释,应该确保描述了离它最近的代码,非公共代码中javadoc
5 格式
统一的格式沟通
垂直格式和横向格式
- 格式关乎沟通
- 垂直格式
- 大多数200行,最多500行
- 跟报纸学习,先头条,后大纲,后细节;自顶向下,垂直顺序
- 概念垂直方向用空格间隔,在封包声明,导入声明和每个函数之间
- 垂直方向上的靠近,接近的变量不要用注释或者其他隔开了
- 垂直距离 相近的靠近有序
- 变量声明:函数或类顶部或者底部
- 实体变量:java类的顶部,c++类的底部
- 相关函数:调用者应该在被调用者的上面
- 相关概念:相关相近
- 横向格式
- 45个字符左右 40%,10个字符以下 30%
- 水平方向上 用空格隔离或者靠近
- 水平对齐和缩进方法和空范围
6 对象和数据结构
数据和对象,对应成员和方法
过程式代码和面向对象代码
- 数据抽象:
- 对象把数据隐藏在抽象之后,暴露操作数据的函数
- 数据结构暴露其数据,没有提供有意义的函数
- 反对称性
- 过程式代码(只使用数据结构)便于不改动数据结构的情况下,添加新函数
- 面向对象代码,便于不改动既有函数的情况下,添加新的类
- 反之亦然
- Law of Demeter
- 模块不应了解它所操作对象的内部情况
- 如果ctxt是数据结构,自然会暴露内部细节,如果是对象,就不应该
- 不要混杂:既有执行操作的函数,又有公共变量或公共访问器及改值器
- 数据传送对象
- 即数据结构和Active Record
- 不要把这种数据结构当对象使用
7 错误处理
- 使用异常,并用try catch finally
- 使用unchecked异常,符合开闭原则
- 环境说明,异常封装,特例模式,null值处理
8 边界
- 使用第三方代码,封装起来,构建边界,只暴露需要的功能
- 学习性测试,和使用尚不存在的代码,先自己根据业务逻辑定义,先写伪对象测试,等代码实现完之后用adapter封装与api的互动
- 两种方式:封装;adapter模式(接口桥梁)
9 单元测试
- TDD测试生产重构,整洁测试:构造操作检验
- 测试可读性,每个测试一个概念(可以用模板模式分离多个断言,共有的变为基类)
- FIRST
10 类
- 类单一职责(只有一条修改的理由);内聚(少量变量,每个方法尽可能用到这些变量)
- 修改最好少修改(或者打开类),多添加;通过依赖于抽象来实现隔离修改
11 系统
- 全局设置策略,构造与使用分开
- main通过Builder构造对象,然后application使用这个构造的对象;main传抽象工厂实现类对象给application,application然后用抽象类保存实现多态,将构造细节隔离在应用程序之外
- Ioc模式,依赖倒置(上层和下层模块都依赖于抽象),控制反转,依赖注入(上层模块使用哪个具体,通过配置文件或者容器传入或者特殊的构造模块)
- AOP,把相同类型的流程抽出来,用依赖注入(如xml)动态的增添代码;
- 代理模式,通过代理对象访问实际对象;装饰模式,两个类实现同一接口,一个类包含另一个类,再加上其他方法或者成员进行装饰
- 最佳的系统架构由模块化的关注面组成,不同领域通过类或者其他整合起来;模块化和关注面切分
- 合理使用现有标准,领域特定代码DSL能填平领域概念和实现领域概念
12 迭进
- 运行所有测试,测试消除改代码的恐惧
- 重构包括:不重复(模板模式,抽象类有一个方法是final,所有的子类都这样做),表达清晰,尽可能少的类和方法
13 并发编程
- 原则:单一权责
- java ConcurrentHashMap/ ReentrantLock/ Semaphore/ CountDownLatch
- 执行模型:生产者消费者,读者作者,宴席哲学家
- 互斥,线程饥饿,死锁,活锁
14 逐步改进