有乎-Tommy.Li的个人博客

价值、共享、信任

重构(Martin Fowler)之读后感

| 阅读:565 发表时间:2020-03-27 15:54:18 有感

   刚开始因为想读所以拿到本书,加上又有些懒惰,就没有一直看下去。直到有导师任务之后,便可以静下心来,慢慢细读、慢慢消化。当初选择2个sprint(20天)的时间来读,也是为了能够理解的更深,从而收获的更多。那么接下来就从为什么要重构?怎么做?优势在哪里?等几个方面谈谈本书读后的具体感悟吧:

一、目的:

        1、去除重复、简化逻辑、澄清模糊的代码

        2、增加代码的可控性

        3、简化对代码的理解

        4、清除代码中的隐藏Bug

        5、提高编程的速度

        6、改善现有的设计,易于扩展

        7、......(更多)

二、为什么要重构?

    在本书的第3章:代码的坏味道(P75)具体列出了22种不同的需要重构的例子。

    总结归纳为:

        1、代码存在重复(可能是大量或少量) 

        2、逻辑处理过程复杂(一个过程包含了N个逻辑)

        3、代码混乱模糊(强耦合、强依赖关系、隐藏BUG)

        4、难于理解的代码段(过长、过大的类、函数体、条件表达式等)

        5、.......(更多)

正是由于代码存在以上的问题,所以我们需要重新审视代码、对代码进行重新组织。那么我们如何做呢?

三、重构,我们要怎么做?

要考虑重构的做法,本书的第6-12章都在讲重构的手段,也举出了具体的例子(不过是Java的)那么我们都需要考虑那些特性?要从哪些地方开始入手呢?。

    前提:重构是建立在健壮的测试基础之上的 (测试驱动开发)

总结归纳入下:

    1、函数   

    2、类(子类,继承类,变量类及类的多态和继承特性)

    3、数据

    4、条件表达式(if-elif-elif-....、switch-case-case-....)

    5、变量(临时变量,全局变量)

    6、对象

        7、......(更多)

共性:重复,太长,表达不清晰,混乱,难于理解和阅读。

    解决步骤:1、检查       2、声明      3、内联和提炼(上移或下提)

              4、编译,测试 (3,4,3,4,3,4,3,4.......)

              5、找出与此相关的引用点并修改  5、删除         6、编译,测试

              7、......(更多)

 对第3章提到的代码的坏味道,本书也给出了一页第6-12章处理这些坏味道的表格,此表格非常方便,方便做为以后指导用,如下是从网上找了一份归纳图,个人感觉更有实际意义,也可以看本书的最后一页(不过页码比较麻烦,转来转去):

坏味道特征情况及处理方式目标
重复代码1.重复的表达式
2.不同算法做相同的事
3.类似代码
同一个类的两个函数有相同表达式重复代码提取为方法相同表达式只在一个类的一个方法出现,供其他方法调用
兄弟类含有相同表达式重复代码提取为方法
提升方法到父类
不相干类含有相同代码提取为独立类供调用
过长函数1.代码前面有注释
2.条件表达式
3.循环
 提取方法每个方法只做一件事,方法要定义完善、命名准确
过大的类1.一个类中有太多实例变量
2.一个类中有太多代码
部分字段之间相关性高相关的字段和方法提取为类每个类负责一组具有内在的相互关联的任务
某些字段和方法只被某些实例用到这些字段和方法移到子类中
过长参数列1.参数列过长
2.参数列变化频繁
方法可以通过其他方式获取该参数让参数接受者自行获取该参数只需要传给函数足够的、让其可以从中获取自己需要的东西就行了
同一对象的若干属性作为参数在不使依赖恶化的情况下,使用整个对象作为参数
被调用函数使用了另一个对象的很多属性将方法移动到该对象中
某些数据缺乏归属对象首先创建对象
发散式变化一个类受多种变化的影响类经常因为不同的原因在不同的方向上发生变化将特定原因造成的所有变化提取为一个新类针对某一外界变化的所有修改,只应发生在单一类中,而这个类中所有的内容都应反映此变化
散弹式修改一种变化引发多个类的修改某种变化需要在许多不同的类中做出小修改把所有需要修改的代码放进同一个类中针对某一外界变化的所有修改,只应发生在单一类中,而这个类中所有的内容都应反映此变化
依恋情结一个函数使用其他类属性比使用自身类属性还要多某个函数从另一个对象调用了几乎半打的取值函数将依恋代码提取为单独方法,移动到另一对象将数据和对数据的操作行为包装在一起
数据泥团同时使用的相关数据并未以类的方式组织
1.两个类中相同的字段
2.许多函数中相同的参数
 先将字段提取为类,再缩减函数签名中的参数总是绑在一起的数据应该拥有属于它们自己的对象
基本类型偏执过多使用基本类型总是被放在一起的基本类型字段提取类将单独存在的数据值转换为对象
参数列中有基本类型提取参数对象
数组中容纳了不同的对象,需要从数组中挑选数据用对象取代数组
基本数据是类型码使用类替换类型码
带条件表达式的类型码使用继承类替换类型码
Switch语句相同的switch、case语句散布于不同地方根据类型码进行选择的switch使用多态替代switch避免到处做相同的修改
单一函数中有switch使用显式的方法取代参数
平行继承体系1.为某个类增加子类时,必须为另一个类增加子类
2.某个继承体系类名前缀和另一个继承体系类名前缀相同
 一个继承体系中的实例引用另一个继承体系中的实例,然后迁移成员避免到处做相同的修改
冗赘类类无所事事父类和子类无太大差别将它们合为一体 
某个类没有做太多事情将这个类所有成员移到另一个类中,删除它
夸夸其谈未来性 某个抽象类没有太大作用将父子类合并 
不必要的委托将这个类所有成员移到另一个类中,删除它
函数的某些参数未用上移除参数
函数名称带有多余的抽象意味重命名函数名
函数只被测试方法调用连同测试代码一并删除
令人迷惑的暂时字段1.某个实例字段仅为某种情况而设
2.某些实例字段仅为某个函数的复杂算法少传参数而设
 提取单独的类,封装相关代码 
过度耦合的消息链一长串的get_this或临时变量客户类通过一个委托类来取得另一个对象隐藏委托消除耦合
中间人某个类接口有大量的函数都委托给其他类,过度使用委托有一半的函数移除中间人 
少数几个函数直接调用
中间人还有其他行为让委托类继承受托类
狎昵关系某个类需要了解另一个类的私有成员子类过分了解超类将继承改为委托,把子类从继承体系移出封装
类之间双向关联去掉不必要的关联
类之间有共同点提取新类
异曲同工的类两个函数做同一件事,但是签名不同 合并 
不完美的类库类库函数构造的不够好,又不能修改它们想修改一两个函数在调用类增加函数 
想添加一大堆额外行为使用子类或包装类
幼稚的数据类某个类除了字段,就是字段访问器、设置器 1.用访问器取代public字段
2.恰当封装集合
3.移除不需要的设置器
4.搬移对访问器、设置器调用方法到此类
5.隐藏访问器、设置器
封装
被拒绝的馈赠派生类仅使用了基类很少一部分成员函数子类拒绝继承超类接口使用委托替代继承 
过多的注释一段代码有着长长的注释 消除各种坏味道 

三、重构能给我们带来什么?

个人感触如下:1、扩展更容易                2、增加代码的可控性,理解更透彻

              3、代码更优秀,结构更清晰    4、保证较低的Bug率

              5、提高编程的速度            6、代码质量得以保证

              7、团队开发更便捷            8、......(更多)

四、疑问点:

1、应该通过重构实现模式、趋向模式和去除模式,而不是在预先设计时就使用模式?

     本书中讲先实现代码,再趋向于模式,就是重构的过程,这个与现在的思维相反,一般情况下:我们是先设计,再实现。

       2、大量的测试环境的构筑,是否会影响开发的进度?

     本书中提到要先建立健壮的测试环境,然后再进行编码。而重构的步骤是:测试-》小修改-》再测试-》重复小修改的过程...(持续重构)。

3、尽可能的让函数返回一个值?

 本书中前提是要采用减少临时变量的重构方法之后,如果减少临时变量的重构方法不能改变的话,那就用方法对象来替换函数,因为在对象里的临时变量可以有很多。 

4、在使用重构时,每种方法的适当应该之处,例如Extract Method和 Introduce Explaining Variable两种方法都能准确的表达重构的意图,如何使用看个人爱好?还是?

     这个比较难以定义,看完整本书后,基本上清晰,两种都可以用,一步步的提炼,没有最好,只有更好吧!

扩展:模式是重构(必经之路)希望达到的目标,也就是说设计模式是重构的目的。 

      这个东西需要好好理解下,另外,重构中涉及到的工厂模式、策略模式等,都是一知半解,还需要慢慢消化。

*文章为作者独立观点,不代表【uuuho有乎】的立场
本文由【uuuho有乎】发表并编辑,转载此文章须经作者同意,并请附上出处及本页链接。如有侵权,请联系本站删除。