《重构改善设计》重构思想梳理总结


《重构》整本书的内容梳理总结-详见:https://blog.csdn.net/runafterhit/article/details/106416067
下面是个人对书中一些关键思想 梳理总结。
关键词:
重构:对软件内部结构的一种调整,在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本
重构的原因:改善软件设计/提升可读性/帮助寻找bug/提高编程速度
重构时机 :添加功能时 / 修补错误时 / 复审代码时

软件面临困局

1、软件有一个良好的初始版本,考研 设计者 对 需求的理解深度 和 方案的设计能力,追求一个优秀初始版本设计压力 和 困难度 是非常大的。场景分析遗漏,理解出入,过程需求调整,人员变动,人力投入 等 都会产生影响,一旦一个系统相对复杂,很难考虑面面俱到,在开发过程总是会发现 最初设计的种种漏洞。
2、即使软件有一个良好的初始版本,软件的架构设计和编码可读性 如果没有人看护,是会持续腐*的,引入的原因 常见,后续规格开发者对设计理解偏差,或者编码能力较弱、细节考虑不周解决问题的时候 最求短期节点 容易在没有完全理解原有设计下“ 贸然下笔”,引入设计债
3、软件特定在于 ,可塑性非常强,可以说没有什么是不能变化调整的。同一个需求,有大量的设计方案,同一个设计思路,实现编码过程也有 数据结构选择 和 流程设计 差异,更不用说 还存在设计风格的不同。(这也是 设计模式 和 编码规范的价值,设计模式 提供 典型的问题解决方案,而 编码规范 统一编程风格)
4、成功的设计,不是因为用了封装/归一化而成功,而是切合自己面对的问题,给出了恰到好处的设计。面向对象语言 带来 语法糖 的 同时,也 引入了设计难度封装、继承、多态只是语言层面对良好设计的支持,并不能导向良好的设计。如果设计层面做不出真正的封装性和归一化,反而会误导你,使你更快、更远、更诡异的偏离目标。有的java项目需要100个人,而有的项目只需要3个人,功能都一样,需求变更时候,后者响应更快,bug还少。

面对这些困局重构能解决什么问题

重构主要是求一种 对现有设计和实现的持续优化调整,保持设计合理性 和 代码可读性,最求的是一种 动态的平衡
1、重构和设计是互补的,降低设计面临的压力
2、重构能改善代码的持续腐化,提升可读性
3、重构能提升方案的灵活性,更易于添加和修改
4、重构能挽救 不良的设计 或者 在开发周期中 逐渐演变为不良设计的 的代码

如何做重构?重构核心思想是什么?有哪些需要权衡的问题

坏味道、测试先行、行为保持的变更动作,是重构的基本功。重构的核心思想。

1、消除重复—代码坏味道最常见问题,提炼删除。

两个函数 中有相同的代码,用 提炼函数 方式消除重复。两个函数做的事情是一件事情,但是实现方法不一样,选择最优 或者 设计新实现,使用 替换算法 消除重复。
两个互为兄弟的子类 中存在相同的 字段、操作,使用 字段上移 函数上移 移动到超类中,如果只是相识的行为和流程,可以使用 塑造模板函数 把 相同的流程 放到 超类中,把差异化部分 在子类中扩展实现。
两个完全不相关的子类,存在 重复代码,考虑 使用 提炼类 把 相同的放进去。

2、临时变量权衡—消除无意义临时变量/为可读性引入临时变量/引入临时变量替代对入参赋值

总体来说,如果临时变量不能起到 增强可读性的功能,比如一个简单的表达式赋值一次使用,那么 内联临时变量 或者 以查询取代临时变量 尽可能的消除它们。移除控制标记,在循环中使用break,return,替代使用一个临时flag判断跳出。 反之 引入解释性变量 增强复杂代码可读性。如果一个临时变量 被多次赋值,切其含义发生变化 分解临时变量 把每次赋值独立开来,避免混淆。如果过程有直接对入参赋值,移除对参数的赋值 引入临时变量替代。

3、语句可读性优化—表达式与分支 的调整、精简与提炼

卫语句 题替代嵌套的条件表达式,突出正常的执行路径。如果多个地方都需要反复判空,考虑让null本身合法,引入Null对象简化判断。
如果大量分支都 能得到相同的返回只,如多个 异常检查,合并条件表单式 把它们提炼为一个简单函数。
针对复杂的逻辑表达式,如一个复杂的if-then-else语句 分解条件表达式, 三个段落 分别 提炼 独立函数,清楚的表明每个分支的作用,如果多个分支有重复的部分,通过 合并重复条件片段 把 重复部分移到条件分支外 保证 都能执行 。
如果表达式 不同对象类型执行不同行为,使用 以多态取代条件表达式
当字面数值带有特殊含义,通过 以字面常量取代魔法数 让 带有它明确命名提升可读性。

4、函数可读性优化—名副其实/职责单一/层次清晰/入参最小化/查改分离/异常取代错误码

函数名称 和 实际功能不匹配,函数改名 优化。
如果部分短小函数本身 语句可读性已经非常清晰了,内联函数 将其 直接插入调用位置,减少调用层次。如果某些设置函数不被使用或者使用非常受限,比如只能在创建对象或者某个行为间能使用,就 移除设值函数。有一个函数,从来没有被其他任何类用到,将这个函数修改为private 隐藏函数
入参最小化,函数本体不再需要某个参数,移除参数,反之 若需要得到更多信息 或者 两个函数过于类似,通过 添加参数/令函数携带参数 扩展或者合并,同时 如果一个值 引起函数行为差异过大,应该 以函数取代参数 分离它们。
如果某些 参数 始终时成对出现,存在关联性,打包它们 或者 引入参数对象 管理其关联关系。
某一个函数返回一个特定的代码,用于表示某种错误的情况,以异常取代错误码以测试取代异常,面对一个函数可以预先检查条件,先在效用函数前检查条件,优于调用后检查异常。

5、(对象)数据组织调整—对象提替代纯数据/封装读写/引用与值对象选择

如果一个数据数组每个元素代表不同特性,以对象取代数组 ,用明确字段成员表示其含义。数据要配合操作行为才有价值,更能体现含义概念,以对象取代数据值,把纯数据和它的行为提炼到一起。
尽可能少暴露public属性,使用封装字段修改为private并提供访问接口,通过Get/Set访问。如果一些数据要时刻被外部领域访问,比起直接修改,更建议是使用observer 复制“被监视数据” 到 领域对象,需要更新时主动更新它们。
如果类中存在一个不变的类型码 影响类的行为,以类取代类型码 使用子类 取代类型码,如果无法通过继承消除,考虑使用 以state/strategy取代类型码 。相反 如果 子类之间唯一差别 针对 某个返回常量,以字段取代子类
将引用对象改为值对象, 如果一个引用对象,很小且不可变,而且不易管理,就替换为值对象,反之 如果一个类衍生出许多彼此相等的实例,希望将他们替换为同一个对象,可以将值的对象改为引用对象

6、对象特性转移与关系调整-- 继承关系调整/ 属性方法迁移

两个无关联类 有相识性,可通过 提炼超类把相同特性移动到超类中。反之,如果 本身存在继承关系 超类和子类无太大差别,可以 折叠继承体系 合为一体,或者某个类没有做什么有效的事情 类内联化 删除一个类。如果 A类 本身某个 函数 相对自身 对 B类 有更多的交流或耦合,或者 A类中的 某个数据 更多的被B类使用,使用 搬移函数 搬移字段 迁移到B类中去。
存在继承关系的类。发现子类只使用了超类一部分,并不想实现超类全部行为,可以通过 以委托取代继承 消除继承关系,同样的反之,如果两个委托的类 关系非常紧密,经常要编写很多极其简单的委托函数,考虑 以继承取代委托 明确其继承关系。
如果两个子类 的 字段或者 方法 完全相同,使用 字段上移 函数上移 移动到超类,构造函数部分完全一致,同样 构造函数本地上移 移动到超类。相反,如果 超类 中 某些函数和属性 只有 部分(而非全部)子类使用,把它们 函数下移 字段下移 到 对应的子类,或者 提炼子类 建立一个新的类去接受特性。

7、多个对象间关联调整— 委托与中间人调整/梳理分解继承体系/提炼继承体系

用户对象 如果 既需要 直接调A类对象,也需要通过委托 B类 去调用 A类对象,考虑 隐藏委托关系 直接 全部委托 给B类 访问 A,不再看到A类,但是 如果 B出现大量 简单的委托函数(B没有特殊存在价值), 移除中间人 让 用户直接调用A类。
梳理并分解继承体系,某个继承体系同时承担两项责任,建立两个继承体系,并通过委托关系让其中一个可以调用另一个。将领域和表述/显示分离 类似的,将领域与逻辑分离出来,将来更好扩展和替换某一部分。
提炼继承体系 如果发现某个 类 做了太多工作,内部行为存在大量条件表达式,那么就愮 建立继承体系,以一个子类表示一个特殊情况。

8、特殊情况下的扩展—外加函数/本地扩展

当需要对某个服务类添加函数实现,但却不能修改这个服务类的时候,用 外加函数 方式 在客户类建立一个函数,然后以第一个参数形式传入一个服务类实例中,提供服务使用。如果需要添加一组操作,用 引入本地扩展 方式,使用新类(包含额外函数)继承 原类 或者 委托原类。

重构不是银弹,也不是万能钥匙

1、关注代码的生命周期,如果还需要长时间开发维护,或者 基于此基线迭代下一个项目版本,重构投入收益更高。
2、项目的关键阶段不要轻易重构,避免把自己拖入泥潭,反过来影响开发节奏。
3、如果当前的业务代码 没有 完善的测试用例支持,尽可能不要先做重构,先完善用例。
4、如果你的业务代码 现有功能 不通过大部分功能测试用例,不要重构。不要把解决问题和重构绕在一起。
5、如果 代码的可读性较差,不要 轻易做大范围的重构(容易引入问题),进行小范围重构微调提升可读性。
6、在你对业务场景理解足够深刻之前,不要做大范围的重构动作,可以组织新方案的讨论,在开发团队内部多讨论。

猜你喜欢

转载自blog.csdn.net/runafterhit/article/details/106487377