文章目录
写代码的基本理念
1、当需求和代码冲突时:
- 需求不合理时,代码重要
- 需求合理时,需求重要
2、写代码要以测试为驱动
- 写的代码要可以执行单元测试
3、 写代码要简洁:
- 能通过所有测试
- 没有重复代码
- 体现系统里全部设计理念
- 包含尽量少的类、函数、方法
如何命名?
- 实体的名称应回答所有大问题,即:实体为什么存在、做什么事情、应该怎么用
- 避免误导:用注释标明所有可能产生误解代码本意的线索
- 实体名称之间应做有意义的区分
- 使用读得出来、可搜索、不带有类型和作用域的名称
- 类名、对象名使用名词&名词短语, 方法名使用动词、动词短语
- 每个抽象概念对应一个词,并一词一意
函数
- 短小有力
- 只做一件事
- 每个函数一个抽象层级
- switch埋藏在较低的抽象层级且不重复
- 使用描述性、与模块名一脉相承的词语命名函数
- 参数应该尽量少,如果需要两个、三个或三个以上参数,说明一些参数应该被封装成类
- 分割指令和查询:要么回答,要么处理,二者不可兼得
- 抽离try catch
- 结构化编程
注释
- 法律信息
- 提供信息的注释
- 提供意图的解释
- 警告
- TODO
- 放大某些看似不合理的地方
对象和数据结构
- 模块不能了解它所操作的对象的内部信息,对象不应通过存取器暴露内部结构
错误处理
- 将业务逻辑和出错处理隔离开
- 在不适用隔离时创建一个配置对象将特殊情况返回
- 异常处理不能过多暴露细节
边界
- 利用边界清晰分割代码
单元测试
- 在编写不能通过的单元测试前不可编写生产代码
- 只可编写刚好无法通过的单元测试,不能编译也算不通过
- 只可编写刚好通过当前失败测试的生产代码
整洁测试:可读性
- 快速,测试应该够快
- 独立,测试应该相互独立
- 可重复,测试应该可以在任何环境重复通过
- 自足验证,测试应该有布尔值输出,不论测试成败,不能人工通过log确认测试结果
类
- 单一权责原则,类或者模块应有且只有一条加以修改的理由(一个类不能承担多于一个权责)
- 内聚性:一个类中的每个变量都被每个方法所使用,则该类具有最大的内聚性,内聚性越高,逻辑越严密
系统
- 将系统的构造和使用分开
- 将起始过程和起始后的运行逻辑分离
- 分解main,将程序构造放入main并与运行时逻辑分离,main只管构造并将构造后的数据对象发给各个应用或者被各个应用使用但不参与任何逻辑控制
- 工厂模式:将系统构造和系统运行时逻辑分离
- 依赖注入
- 扩容
- 迭代和增量敏捷的精髓:实现今天的需求,然后重构或者扩展
- 测试驱动系统架构(面向切片编程)
- 优化决策
迭进
- 运行所有测试
- 多写测试
- 持续运行测试
- 不可重复
- 表达了程序员的意图
- 尽可能减少类和方法的数量
重构原则
-
提升内聚性
-
降低耦合度
-
切分关注面
-
模块化系统性关注面
-
缩小函数和类的尺寸
-
选用更好的名称
-
不可重复
-
表达力
-
尽可能少的类和方法
并发编程
- 并发:解耦策略:把做什么和何时做分开
- 并发的防御原则
- 单一权责原则
- 并发相关代码有自己的开发、修改、调优生命周期
- 并发相关代码有自己要对付的挑战,和非并发相关代码不同,而且更为困难
- 建议分离并发相关代码
限制数据作用域
- 多线程修改共享对象同一字段可能引起相互干扰
- 解决方案:
- synchronized关键字保护一块使用共享对象的临界区
- 锁
- 避免共享数据
- 有可能复制对象并以只读方式对待
- 有可能复制对象从多个线程收集结果,并在单线程合并
- 线程尽可能独立
执行模型
-
生产者-消费者模型
- 一个或多个生产者模型创建某些工作,置于缓存或队列中,一个或多个消费者线程从队列中获取并完成这些工作,生产者和消费者之间的队列是一种限定资源
-
读者-作者模型
- 当存在一个主要为读者线程提供信息源,但只偶尔被作者线程更新的共享资源==》吞吐量问题
- 增加吞吐量==》线程饥饿&过时信息积累
- 更新==》影响吞吐量
- 协调读者线程,不去读作者线程正在更新的消息==》痛苦的平衡工作
-
哲学家问题
警惕同步方法之间的依赖:
-
避免使用一个共享对象的多个方法
-
如果一定要使用同一个共享的对象的多个方法:
- 基于客户端锁定:客户端代码在调用第一个方法前锁定服务端,确保锁的范围涵盖了调用最后一个方法的代码
- 基于服务端锁定:在服务端内创建锁定服务端的方法,调用所有方法,然后解锁,让客户代码调用新的方法
- 适配服务端:创建执行锁定的中间层,是一种基于服务端的锁定,但不修改原服务端代码
保持同步区域微小
- 尽可能少的设计临界区
- 尽可能减少同步区域
编写线程时:
- 不要将系统错误归结为偶然事件
- 先使非线程代码可工作,不要同时追究线程缺陷和非线程缺陷,确保代码在线程之外可以工作
- 编写可插拔、可调整、可在数个环境运行的线程代码
- 运行多于处理器数量的线程
- 在不同平台上运行
- 调整代码强迫错误发生
不恰当的注释:
- 注释仅应该描述有关代码和设计的技术性信息
- 过时、无关或不正确的注释应当废弃
- 冗余注释:注释了充分自我描述的东西即为冗余
- 看到注释掉的代码就删
环境
- 应该用单个命令构建环境
- 应该快速简单的运行全部单元测试
函数
- 函数参数尽量要少
- 永不被调用的方法应该废弃
- 尽量消除重复
类
-
基类应对派生类一无所知
-
接口要小,不要创建或为子类创建大量受保护变量和函数,尽量保持接口紧凑,通过限制信息来控制耦合度
-
类中的方法越少越好,函数知道的变量越少越好,隐藏数据、工具函数、常量、临时变量
-
删除死代码
-
私有函数应恰好在首次被使用的位置下面定义
-
删除无用的变量和函数
-
类的方法只应对其所属类中的变量和函数感兴趣
-
把逻辑依赖改为物理依赖
- 逻辑依赖:原始数据和业务逻辑之间的依赖关系,如定义的常量和业务逻辑存在依赖关系或者被捆绑在一起时,即当业务逻辑发生变化或者存在其他可能性时,且定义的常量无法满足需求,导致常量需要修改
- 物理依赖:函数方法和业务逻辑之间的依赖关系,为了解决逻辑依赖,可以构造一个方法,将数据和业务隔离:即逻辑依赖改为物理依赖
-
用多态替代if/else或switch/case
-
遵循标准约定
-
结构最大
-
封装条件:将包含有条件意图的函数抽离出来
-
避免否定性条件,尽可能将其表现为肯定形式