第一部分
第一章:
- 系统重构的基本前提:不变更软件的外部行为,尽量减少变更接口的输入输出
这也是重构的保险索 - 测试的建议:
刚开始重构时只测试大功能;耦合松散后,开始各个类与接口的自动化测试
每个小步都要频繁测试,小步快跑避免泥团性错误 - 大布局与小步快跑
设计阶段:有限的预见。避免过于虚幻。如果今天的设计不符合明天的需求,尽管重构就是,重构不是专门抽时间来做的事情,而是无时无刻都应该去做的事情,而是常态。
大布局中的一个小建议:领域驱动设计。设计中的关系与真实世界中的关系保持一致,如果设计不清晰,则说明对于真实的认知不深入、缺乏提炼。
真不能满足需求时在两顶帽子下进行重构保证后期功能可用——小步快跑修改
第二章+第三章:重构方法工具箱
- 重构的理解:重构实质上是一系列的等量变换
以方法重构为例:将拥有诸如没有注释、顺序编程、没有层次、内聚度低等坏味道的代码,通过增加注释、调整顺序、重命名变量、进行分段等手段进行等量变化,结果还是那些结果,但是整体变得更可读、可维护、易变更。 - 重构的粒度与顺序
抽取方法:抽取每一个下一步行动,并思考这些方法合适的位置,由此开启抽取类
抽取类:面向对象,对象是有生命的对象,方法是对象的具体行为,方法与对象绑定,单一职责等七大原则,高内聚低耦合等,自己在自己的独特范畴内运行生效,同时也突出了分级
抽取接口:分离变化与不变,实际上是面向变化,一个变化点就是一个重构点
好的框架最核心实际上就是单一职责,有了一个新需求新改动,只需要改一个很具体的类的一个很具体的方法的一小段部分,其他都不需要任何变动。这就是抽取类(高内聚低耦合隔离变化与依赖)、抽取接口(识别变化点,更有弹性)的好处
补充参考:
类的设计原则:https://blog.csdn.net/qq_34760445/article/details/82931002
重构后的样子要仔细与这七大原则一一对应
第五章
- 大函数形成的原因出发:
软件发展的规律就是业务逻辑越来越复杂,需要适时重构优化代码,而不是遇到什么情况都向旧函数中塞。 - 实际上重构的突破口就是代码坏味道。
最常见的突破口就是大函数——抽取方法、超级类——抽取类 - 抽取方法的几个步骤:
1.分段注释(尤其是语句块)、调整顺序、变量重命名、同类代码放在一起
2.抽取代码为独立的方法,遵循高内聚、低耦合原则,一个完整、独立、清晰的功能。
3.抽取方法时名字的思索有必要仔细思考,命名需要符合领域驱动设计,让命名之后函数自明,代码应该表现出自己的目的。
越容易被人读懂的代码越是好代码——第二个读者才是最重要的。
4.重复的代码有必要抽取、块操作也是抽取的合适时机
5.最大的困难,处理抽取函数与原函数的数据交互
6.数据交互处理技巧:1.值对象提取(最终随着对代码理解的加深,并通过领域驱动设计,找到值对象在现实中的意义)。2.巧用Java内存,入参塞值的方式
实践落地: - 拆分大方法为一系列下一步行动
- 拆分时序图,识别要提炼的方法
第六章:
- 大对象的定义:无所不包,一个对象搞定所有事情。
几十行无所谓,业务不复杂可以一波
过百行就要开始思考对象是否有问题
过五百行就铁定有问题
大对象的产生即推土机编程,所有都放在一个类里
2. 大对象拆分的核心原则:职责单一原则与抽取类
职责驱动设计思想:各个类职责单一,高内聚低耦合,聚焦自己的业务,完整处理职责范围内的所有事情,对其他类的依赖尽可能低、不去动其他类的业务、获取信息尽可能少
职责单一的演变:
引起软件变化的原因:一个变化点就是一个职责,我的变化不能影响你的职责,你的变化也不能影响我的职责,所以一个变化点就是一个职责。
在做类的重构时,先不要考虑技术上难易的问题——技术上是一定能够实现类的设计与交互的,考虑这些反而影响类的设计。
三种方法:
分析领域模型:即与真实世界对应,职责保持一致。而这个职责,实际上来自于软件变化的一个具体原因。
分析变化的原因:一个原因/变化点前期可以直接抽取出一个类,后期理清楚同类业务后再去将对同一操作对象的操作聚合。即先拆分后聚合法。
同时结合分析领域模型,要有类族的概念,这几个类共同服务于领域模型中的某个大功能块,但是各自在其中承担了一部分职责,他们共同构成了一个族。变化的原因即为在族中圈分类的一个手段。
寻找信息专家:一类信息的拥有者、管理者、处理者。比如掌控所有权限信息、掌控所有用户通用信息、掌控所有用户密码信息等
实践落地:
- 代入license类思考每一章节的理论,根据时序图以及抽取方法,重新细分职责
真实世界业务划分
变化点抽离与聚合
寻找信息专家
第七章:消除重复,提高复用率
- 绝对不允许出现代码重复!
- 重复代码在同一个对象中:抽取方法。
哪怕只是一直出现的一句话也要抽一个单独方法,方便统一修改 - 重复代码位于不同对象中:抽取类、接口
散弹枪式修改的解决方案:
提取util工具类(无需实例化,全是各种操作方法,与输入关系不大)
提取实体类(需要实例化,不同位置调用时有一定先决依赖,比如filter) - 代码所在类具有某种并列关系:抽取父类
- 继承泛滥时:继承转化为组合
- 碎片式的重复代码与不同代码:模板模式
落地实践:
- 思考license的demo与lic情况下抽取抽象方法的做法
实际上总是不尊重事实、不尊重客观规律、逆天而为。强行细致,啃那些没有任何技术铺垫的东西,做那些与现实关联不大的东西。不顾及/刻意忽略大脑发出的信号,不去灵活思考,只是强行去做去蛮干。
第八章:扩展点
- 开放封闭原则阐释:对扩展开放,对修改封闭。遵循开闭原则设计出的理想代码:在增加新功能、新改动的时候能够在不修改原有代码的基础上完成。实际上就是做好隔离:高内聚低耦合
但是一般情况下,设计之初无法看到这么多的可扩展点,而且也无需过度设计。因此需要在重构的时候遵循两顶帽子,先加入扩展点,之后对类似功能的增加修改开闭。
为更好满足开闭原则,一定要提前识别可扩展点 - 代码坏味道:ifelse之处就是极容易破坏开闭原则的地方。
config配置文件取代ifelse是一种常见做法。 - 模板模式与钩子函数(abstract基类)增加可扩展点。
适用于拥有相似操作步骤的一系列同类操作。 - spring的面向AOP编程拦截器的配置是典型的配置文件处理方式。
适用于某些操作前后具有不确定的操作步骤,比如权限校验、输入输出检测等。 - 其他可扩展性方式:
现有的类无法完全满足业务需求:代理(组合)与继承。 - 最后,可扩展点要求去识别客户需求,识别改动点,一个改动就是一个原因,而且要识别改动策略的多样性,客户提出了一种可能的策略势必也会有第二种。
落地实践:
- 开闭原则去看当前类的设计:哪些对扩展开放对修改封闭?
- If else的去除
第九章:设计模式
- 工厂模式:系统设计层面。
从依赖反转原则说起:1.高低层次都应该依赖于接口。2.具体实现依赖于接口。
即设计应当依赖于抽象而不是实现
工厂模式的核心:工厂类。 - 外部接口与适配器模式:外部库的解耦与兼容后的优雅调用。
- 桥接模式与单例下的桥接模式:接口混搭
- 策略模式+命令模式:串行执行一系列命令,并实现随机插入移除
- 组合模式(总包)与装饰者模式(基础上去做)
落地实践;
- 适配器模式修改代码:让调用更加优雅
- 适配器与工厂如何动态搭配?
第十章:
-
常见宏观架构上的分层:前端(传递用户数据)-》MVC(值对象接收数据)-》BUS(业务逻辑)-》DAO(后端数据)
-
领域驱动设计思想下,贫血模型、充血模型。service层的厚薄问题。
-
分层的视角去看类的设计。什么层级的就去做什么层级的事情。