重构方法与实践笔记

2021-07-05 09:56:20 浏览数 (2)

1.代码的改写从大范围到小范围大致可以分为四级:系统级别,功能级别,代码级别,机器级别; 2.代码级别以下改动可视为“重构”,功能级别以上级别只能视为“重写”

3.重构是持续的日常过程,而重写不是

辨析了“重写”与“重构”之后,下面专注地讲一下重构

1.1 重构的概念和背景

  1. EPC
  2. 破窗理论与懒惰:在没有刻意优化下,代码腐烂是必然的
  3. 80%在别人的代码上进行修改

1.2 重构的目的:使软件结构更加合理

- 1.2.1 WHAT:

在不改变可观察行为下,修改代码内部结构

- 1.2.2 WHY:

差的设计在后期越来越难以新增功能,好的设计在软件开发的每个阶段新增功能的速度都是差不多的

- 1.2.3 WHEN:

童子军法则,只要修改完比修改后更易懂,大到设计,小到函数,命名

- 1.2.4 HOW :
-1.2.4.1 构建测试安全网
代码语言:txt复制
	1. 使用单元测试;分层测试;UI自动化测试
	2. 进行可测试性改造:UI分离;Mock;依赖注入
- 1.2.4.2 消除代码坏味道
代码语言:txt复制
	**1. - 函数问题(30s能读懂)**
代码语言:txt复制
- 过长函数(Long Method):最好不超过20行
- 过长参数列(Long Parameter List):最好不超过5个《代码整洁之道推荐不超过3个》
- 基本类型偏执(Primitive Obsession)
- 重复的Switch(Repeat Switch)
- 循环问题(Loops)
2.单个类问题
代码语言:txt复制
		- 过大的类(Large Class)
		- 临时字段(Temporary Field)
		- 数据泥团(Data Clumps):大量的数据要用结构组织,不要平铺
代码语言:txt复制
		- 纯数据类 (Data Class)
		- 神秘命名(Mysterious Name)
		- 全局变量 (Global Data)
		- 可变数据 (Mutable Data)
- 3. 类关系
代码语言:txt复制
		- 发散式变化(Divergant Change)
		- 散弹式修改(Shotgun Surgery)
		- 依恋情节(Feature Envy)
		- 夸夸其谈通用性(Speculative Generality)
		- 过长的消息链(Message Chain)
		- 中间人(Middle Man)
		- 内幕交易(Insider Trading)
		- 被拒绝的馈赠(Refuse Bequest)如子类不需要父类功能特性, **组合而不是继承**
- 4. 需要删除
代码语言:txt复制
		- 注释(Comments): 代码自注释 > 写注释  > 不写注释
		- 重复代码(Duplicated Code)
		- 冗余元素(Lazy Element)
		- 异曲同工的类(Alternative Class with Defenter interface )
- 1.2.5 工具
代码语言:txt复制
	- SonarLint 插件   CodeDog
	- IDE Refactor    Complex Method

2. 函数重构

2.1 工具

  • ApprovalTest Coverage = 无脑单测
  • CombinationAppovals.verifyAllCombinations
  • Android Studio IDE 覆盖率设置 设置Tracing 格式的可以查看单测命中率 - Preference -> Coverage -> Replace active suites with the new one - Edit Configuration -> Junit -> Tracing

2.2何时重构?

  • 当你想要写注释时

2.3 大函数改造

  • Bloaters - Long Methodundefined维护者未必无法识别Bad Code Smell,重构难,懒惰心理等问题使得coder 躺平。 - 难以维护 - 容易出现bug - 破窗效应
  • 优秀函数的原则:
  • 函数一般写10行
  • 超过20行就考虑重构
    • 第一条规则是短小
    • 第二条规则还是短小
  • 如何处理条件语句
代码语言:txt复制
- 函数提取:即按照逻辑拆分子函数。
- 分解表达式
- 以多态处理堆叠的条件表达式(如switch)
代码语言:txt复制
	- 状态模式
	- 策略模式
代码语言:txt复制
- 将条件表达式转换为查找表,使用注解完成映射

2.4 进阶优化

  • 组合函数(Composed Method)
代码语言:txt复制
- 封装细节
- 保留关键函数路径
- 抽象层次一致
- 最好不要超过10行
- 函数自注释
  • 过长参数
代码语言:txt复制
- 问题
代码语言:txt复制
	- 调用参数不易传递
	- 增加理解难度
	- 伴随巨大函数,基本类型偏执
代码语言:txt复制
- 解决方案
代码语言:txt复制
	- 构造参数对象
	- 用builder 代替构造器
	- 卫语句,即异常case先返回,主要逻辑在后。将嵌套逻辑扁平化
	- 管道替代循环,声明式替代命令式

3. 类重构

Program to an interface, not an implementation.

组合优于继承

3.1 重构为啥难?

    1. 粘滞性
    1. 个人因素
代码语言:txt复制
- 代码阅读能力
- 重构方法的掌握
    1. 环境因素
代码语言:txt复制
- 时间,如需求倒排

3.2 面向对象

  • 3.2.1 三大特性
代码语言:txt复制
- 抽象
- 继承
- 多态
  • 3.2.2 设计原则 使用接口进行解偶
代码语言:txt复制
- 单一职责
- 接口设计
代码语言:txt复制
	- 依赖倒置原则
	- 接口分离原则
	- 接口隔离原则
代码语言:txt复制
- 一个类要尽可能不依赖外部
代码语言:txt复制
	- 高内聚、低耦合
	- 开闭原则
	- 迪米特法则
代码语言:txt复制
- 如何处理父子关系
代码语言:txt复制
	- 里氏替换原则
	- 合成、聚合原则
  • 3.2.3 四个要素
代码语言:txt复制
- 3.2.3.1 抽象:提取关键元素
代码语言:txt复制
	- 三原则
代码语言:txt复制
		- DRY 原则:Don't Repeat Yourself
		- YAGNI 原则:极限编程,不需要抽象那些你不需要的东西
		- Rule of three: 原则1,2的取舍方法
代码语言:txt复制
	- 抽象不足
代码语言:txt复制
		- God Project:违反了单一职责原则
代码语言:txt复制
		  Android 刚开始开发时,使用MVC,V与C混杂在一起,造就了很多God Object。
代码语言:txt复制
			- 提取类
代码语言:txt复制
	- 抽象过度
代码语言:txt复制
		- 如没有变量,只有方法,则抽象过于具体
代码语言:txt复制
- 3.2.3.2 封装:隐藏细节
代码语言:txt复制
	- 封装细节
代码语言:txt复制
		- 1. 成员变量,一般设置为private
		- 2. 集合:Collections.unmodifiableList
		- 3. 函数:暴露较少接口,慎重写public函数
代码语言:txt复制
	- 封装可预期变化
代码语言:txt复制
		- 1. 散弹式修改,如每个AIDL调用,新增时非常复杂(使用查找表 注解依赖注入完成自动映射,不必每次新增)
代码语言:txt复制
- 3.2.3.3 模块化
代码语言:txt复制
	- 实现手法
代码语言:txt复制
		- 通过封装得到模块
		- 模块之间使用接口交互
代码语言:txt复制
	- 常见问题
代码语言:txt复制
		- 模块接口异常
代码语言:txt复制
			- 臃肿 如Refuse Bequest
			- 多变
代码语言:txt复制
		- 模块依赖异常
代码语言:txt复制
			- 循环依赖
代码语言:txt复制
				- 修改一处,相互影响,产生震荡
				- 切断循环依赖
				- 继承可能也产生循环依赖
代码语言:txt复制
			- 中间人依赖
代码语言:txt复制
				- 依赖方个数   被依赖方个数越大,(一般)出现问题可能性越高。
代码语言:txt复制
- 3.2.3.4 层次结构
代码语言:txt复制
	- 常见问题
		- 继承关系复杂
		- 不恰当的继承(Stack -> vector ,不成立的继承关系)
代码语言:txt复制
	- 解决方案
		- 确保可替换性
		- 组合优于继承
		- 依赖顺序正确,最好是层级次序
		- 继承结构简洁,如2层

推荐书籍

  • 重构
  • 设计模式
  • 代码整洁之道
  • 重构与模式

工程师素养

0 人点赞