重构之问题代码

代码需要重构,重构的When和How固然重要,可倘若不弄清楚Why的话,就相当于上知天文、下知地理却弄不明白做饭要先淘米一样,所以什么时候需要重构,什么代码需要重构也是问题的关键,如果将代码的问题提炼出来,什么情况用什么方法才是关键,实践是检验真理的唯一标准!
下面是《重构》第三张罗列出来的集中“坏味道”代码,与其说的这么美观,不如直接叫做问题代码,当然问题代码不代表就是真有问题,而是不符合设计原理,不易读,不易维护等问题,可能屎山能够正常运行,但是再屎山上继续叠需求是很困难的等等问题······

问题代码:

1.重复代码:
情况①:两个方法里面有相同的代码片段(功能相同)。
解决方案:将重复部分提炼(Extract Method)出来,然后再使原来部分调用该提炼的方法。
情况②:两个兄弟类含有相同的表达式。
解决方案:将重复部分提炼(Extract Method),再将提炼的方法写进父类(Pull Up Method)

2.长函数:
此处夹带一点私活:作者在此说拥有短函数的类往往存活时长比较好,比较长,[间接层]所带来的全部利益——解释能力、共享能力、选择能力都是由小型函数支持的。
多数情况使用Extract Method可以将Long Method改为Small Method
特殊情况:
①临时变量繁多时,运用Replace Temp with Query。将临时变量用Query Method(查询式)代替,这样其他方法也可调用。
②参数列表过长时,使用Introduce Parameter Object,将多个参数整合为一个参数对象,使用Preserve Whole Object,将多个同属于一个对象的参数修改为传入该对象。
③上述方案都不可行的话,终极解决方案,Replace Method with Method Object,将该方法改造进入一个特有的对象。
PS:条件式和循环式常常是Extract Method信号,可以使用Decompose Conditional处理条件式(即将判断式独立出为一个方法),用Extract Method处理循环式。

3.过大类
一般来说上帝类其实是最不应该存在的代码,要符合设计原则之单一职责,后期也很好维护,阅读等。
情况:过大类里如果包含了可以分类的变量和方法。
方案:考虑使用Extract ClassExtract Subclass,将可分为一类的的变量和方法聚合为另外一个新类,如果变量和方法其他实例的使用频率低,则可以抽出来为一个子类,还可以面向接口编程,将方法抽象为接口Extract Interface,然后原类去继承实现接口。

4.过长的参数列:
同上述问题3中的情况②和③。

5.发散式变化
简述一下发散式变化,就是如果我要改一个功能导致会修改同一个类中的几个函数,这就是发散变化。
方案:Extract Class将对象以功能为主题拆分为几个类。

6.散弹式修改
散弹式修改就是功能增加或者修改时,导致我们要修改几个类中的函数,与5比较起来就是散弹就是几个类都会产生变化,这使得修改和维护就比较麻烦,浪费大量时间在几个类之间切换修改,也难以调试。
方案:Move MethodMove Fields放进同一个class中,如果没有合适的class通常可以用inline Class

7.依恋情结
在Java中经常出现的Get方法获取某个类的成员变量值,但是如果另外一个类中某个函数大量调用某个Data Class中的数据就会大量去调用Get方法,这就是依恋情结,但是C#中是有属性(自带setter;getter;)这种东西的。
方案:视情况而定使用Move Method还是Extract Method

8.数据泥团
如果有很多类都会定义同一些数据,比方student类和teacher类都会有身高,体重等参数时。
方案:可以将相关参数写进Extract Class中,并且使用Introduce Parameter ObjectPreserve Whole Object 修改相关调用类的相关方法签名,改善问题3的出现。

9.基本型别偏执
作者大概所讲就是数据的基本类型就那几种,如果要描述一个东西需要几个基本类型,就可以将该东西提炼为一个类。
方案:C#中,结构体也是一种很好的东西,ECS框架中就倡导使用结构体保持各中数据类型且字节对齐。作者提倡的方法,Replace Data Value with Object,将这些东西放到一个类中抽象为一个对象,比如上述就可以抽象为身体参数对象,还可以用Replace Type Code With SubclassReplace Type Code With State\Stategy

10.Switch 惊悚现身
应该少用Switch或者case语句,因为添加新判断时可能需要修改以前的所有条件,此时就该考虑多态来解决该问题。
方案:Switch 语句往往很多时候都是以类型码(枚举)来判断,先使用Extract Method将case下的语句提炼到一个函数中,再Move Method该函数至其他多态类中,此时需要决定Replace Type Code With SubclassReplace Type Code With State\Stategy,完成继承结构后运用Replace Conditional With Polymorphism。如果多态显得杀鸡用牛刀的话,完全可以使用Replace Parameter with Explicit Methods

11.冗赘类
存在的意义不大,使用率不高,完全将方法挪走,删掉该类。
方案:删除类,如果是subclass则Collapse Hierachy,如果是组件类,则Inline Class。

12.夸夸其谈未来性
情况:abstract class 没用时就用 Collapse Hierarchy
情况:非必要委托 用 Inline Class
情况:函数的参数没被用上 就 Remove Parameter
情况:函数名与功能不符 Rename Method

13.令人迷惑的变量
如果一个变量的存在仅仅是为了一种很不常见的情况而定义。
方案:请使用Extract Class为这个可怜的孤儿创造一个家,把所有的关联函数都放到这个新家;还可以用Introduce Null Object在变量不合法时创造一个Null变量。

14.过度耦合的消息链
就是获取一个数据时会出现多层引用类似:Human.Head.Lip.Skin,为了获取到嘴唇皮肤就需要这样去拿数据。
方案:Hide Delegate就可以解决上述问题,比如直接在Head层写一个GetLipSkin的方法。通常还可以使用Extract Method方法,再将这个独立函数推入消息链。

15.Middle Man
过多的委托其实是过度运用委托,类似有人想预约董事长的见面,还要向秘书申请,因为不能直接和董事长见面约时间。
方案:Remove Middle Man 直接和负责类对话,减少中间类的委托中转,也可以用Inline Method把他们放进调用端,甚至如果Middle Man有一些其他行为,可以Replace Delegation With Inheritance,直接编程负责对象的subclass。

16.狎昵关系
两个类间互相依赖,总是调用对方的过多属性
使用Move Method(搬移函数) 和 Move Field(搬移字段) 来使字段或函数放在正确的位置,避免2个类之间过多的交互。
也可以尝试使用 Change Bidirectional Association to Unidirectional 来减少耦合。
如果两个类实在分离不开,使用 Extract Class把两者合并到一块。也可以尝试 Hide Delegate把互相之间的访问委托给第三方来斩断直接的访问。
如果是继承关系,子类不需要父类中过多的内容,运用 Replace Inheritance with Delegation来处理不恰当的继承关系。

17.异曲同工类
情况:两个函数做同一件事但是签名不同。
方案:Rename Method 根据其用途重命名,还可能会反复使用Move Method将某些行为移入Classes,甚至可使用Extract Subclass

18.不完美的程序类库
类库里面有些函数不能实现目标,达到我们想要的目的,正如排序函数有,我们通过排序能够找到最大最小,但是我们想找第K大怎么办。
方案:如果只是想修改类库中的一个函数,可以运用Introduce Foreign Method,如果想要添加一大堆额外的功能,要运用Introlduce Local Extension

猜你喜欢

转载自blog.csdn.net/weixin_43381316/article/details/125149706
今日推荐