Duplicated Code(重复代码)
常见的重复代码出现场景:
同一个类的两个函数含有相同的表达式:提炼出重复的代码,然后让这两个地点都调用被提炼出来的那一段代码;
两个互为兄弟的子类内含有相同表达式:对这两个类进行提炼函数,再将提炼出的代码上移,放入超类:
如果代码之间只是类似,并非完全相同,那么就将相似部分和差异部分分割,构成单独一个函数;如果有些函数以不同的算法做相同的事,可以选择其中一个,使用替换算法将其中一个替换掉。 两个毫不相关的类出现重复代码:可以将其中一个类进行提炼,将重读代码提炼到一个独立类中,然后在另一个类内使用这个新类。
Long Method(过长函数)
可以遵循的一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。
如果函数内有大量的参数和临时变量,会被函数重构造成阻碍,因为会把许多参数和临时变量当作参数,传递给提炼出来的新函数,导致可读性降低。
可以在同一个类中,创建获取这个临时变量的getter方法,来取代这个临时变量;那么传入一个对象,可以使参数列表变得简洁一些;如果仍然有很多临时变量和参数,就应该使用"函数对象取代函数"。
如何确定该提炼哪一段代码呢?
寻找注释;
条件表达式和循环;
条件表达式的提炼:分解它;循环的提炼:直接放到一个独立函数中;
Large Class(过大的类)
如果想利用单个类做太多事情,内部就会出现太多实例变量;
可将几个变量提炼至新类,提炼时应选择类内彼此相关的变量,将它们放在一起;或者让它成为要一个子类;确定客户端如何使用它们,然后为每一种使用方式提炼出一个接口;
Divergent Change(发散式变化):一个类受多种变化的影响
如果某个类经常因为不同的原因在不同的方向上发生变化,发散式变化就出现了。
比如新增一个数据库,就必须修改三个函数;新出现一种工具,就必须四个函数等等;
此时也许将这个对象分成两个会更好;
针对某一外界变化的所有相应修改,都只应该发生在单一类中,而这个新类内的所有内容都应该反应此变化。
所以,应找出某特定原因而造成的所有变化,将它们提炼到一个类中;当然,
Shotgun Surgery(散弹式修改):一种变化引发多个类相应修改
在遇到某种变化时,需要在许多不同的类内做出许多小修改。如果需要修改的代码散步四处,你不但很难找到它们,也很容易忘记某个重要的修改;
这种情况下,应将所有需要修改的代码放进同一个类中;
Feature Envy(依恋情结)
某个函数为了计算某个值,从另一个对象那里调用几乎半打的取值函数。
把这个函数移至另一个地方;
某个函数中只有一部分代码需要很多取值函数。
把这一部分代码提炼出来,移动到另一个地方;
一个函数往往会用到几个类的功能,那么它究竟该被置于何处呢?
原则:判断哪个类拥有最多被此函数使用的数据,然后就把这个函数和那些数据摆在一起;
Data Clumps(数据泥团)
有时你会发现:两个类中相同的字段,许多函数签名中相同的参数。首先找出这些数据以字段形式出现的地方,将它们提炼到一个独立对象中;可以尝试删掉众多数据中的一项。这么做,其他数据有没有因而失去意义?如果它们不再有意义,这就是个信号:你应该为它们产生一个新对象;
Primitive Obsession(基本类型偏执)
大多数编程环境都有两种数据:
结构类型允许你将数据组织成有意义的形式;基本类型则是构成结构类型的积木块;
我们不愿意在小任务上运用小对象,像是结合数值和币种的m
oney类,由一个起始值和一个结束值组成的range类,电话号码或邮政编码等等的特殊字符串;
可以尝试使用"对象替换数据值",将原本单独存在的数据值替换为对象;
Switch Statements(switch现身)
少用Switch语句;
出现该语句时,可考虑使用多态替换它;可将swtich提炼到一个独立函数中,再将它移动到需要多态性的那个类里;如果是在单一函数中出现该语句,可以使用由明确命名的函数来取代参数;如果有一个选择条件为null,可以考虑引入null对象;
Parallel Inheritance Hierarchies(平行继承体系)
这种情况,每当为某个类增加一个子类,必须也为另一个类相应增加一个子类;
消除的一般策略:让一个继承体系的实例引用另一个继承体系的实例;
Lazy Class(冗赘类)
如果一个类在重构后的没有价值,就应该消失;
Speculative Generality(夸夸其谈未来性)
我们可能会为系统添加一些未来可能会用上的类,这么做会造成系统难以理解和维护;
如果函数或类的唯一用户是测试用例,也属于这一类问题;
如果它们的用途是帮助测试用例检测正当功能,可以保留它们;
Temporary Field(令人迷惑的暂时字段)
问题对象:内部某个实例变量仅为某种特定情况而设;这样的代码会让人迷惑,因为人们通常认为对象在所有时候都需要它的所有变量;
我们将这种变量提炼出来,单独放在一个类,并将所有与该变量相关的代码都放进来;或者引入Null对象,在"变量不合法"的情况下创建一个Null对象,避免写出条件式代码;
如果类中有一个复杂算法,需要好几个变量,也可能导致这种情况。因为我们不希望传递一长串参数,所以将这些参数都放在字段中。但是这些字段只在这些算法中才有效,其他时候又会使人产生迷惑。
将这些变量提炼出来,提炼出来的新对象就是一个函数对象;
Message Chains(过度耦合的消息链)
当用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象。。。这就是消息链。
这种情况意味着客户代码将与查找过程中的导航结构紧密耦合。如果对象间的关系发生任何变化,客户端就不得不做出相应修改;我们可以先观察消息链最终得到的对象是用来干什么的,看看能否将它提炼出来放到一个独立函数中,在将这个函数推出消息链;
Middle Man(中间人)
封装往往伴随委托,比如我们获取一个类中的字段这件事,就委托给了g
etter方法;我们可能过度依赖委托,如果这样的函数只有少数几个,我们可以使用内联函数,将它们放进调用端;如果这些委托还有其他责任,就把它们变成实则对象的子类;
Refused Bequest(被拒绝的遗赠)
子类继承超类的全部函数和数据。但有时它们只是需要超类中某几个字段和函数,怎么办?这意味继承体系设计错误。
传统做法:你可能需要为这个子类重新建造一个兄弟类;再将不需要的字段或者函数下移给这个兄弟类;或者,所有超类都应该是抽象的;
并不是每一次出现这个问题,都需要使用这种传统做法;因为继承所带来的复用便利性很好,没必要为此太过困惑;
转载请注明原文地址: https://www.6miu.com/read-48378.html