重构,改善既有代码的设计

最近在读《重构,改善既有代码的设计》这本书,整理了些书中对于一些代码优化的笔记,暂时先更这些吧。

代码优化/重构的一些手段/技巧:
【1】提炼函数(Extract Method)
【2】函数上移(pull up Method)
【3】塑造模板函数(Form Template Method)
【4】提炼类(Extract Class)
【5】以查询取代临时变量(Replace Temp with Query)
【6】引入参数对象(Introduce parameter Object)
【7】保持对象完整(Preserve Whole Object)
【8】以函数对象取代函数(Replace Method with Method Object)
【9】分解条件表达式(Decompose Conditional)
【10】提炼子类(Extract Subclass)
【11】提炼接口(Extract Interface)
【12】复制“被监视数据”(Duplicate Observed Data)
【13】以函数取代参数(Replace parameter with Method)
【14】搬移函数(Move Method)
【15】搬移字段(Move Field)
【16】将类内联化(Inline Class)
【17】以对象取代数据值(Replace Data Value with Object)
【18】以类取代类型码(Replace Type Code with Class)
【19】以子类取代类型码(Replace Type Code with subclass)
【20】以State/Strategy取代类型码(Replace Type Code with State/Strategy)
【21】以对象取代数组(Replace Array with Object)
【22】以多态取代条件表达式(Replace Conditional with Polymorphism)
【23】以明确函数取代参数(Replace Parameter with Explicit Methods)
【24】引入Null对象(Introduce Null Object)
【25】折叠继承体系(Collapse Hierarchy)
【26】移除参数(Remove Parameter)
【27】函数改名(Rename Method)
【28】隐藏“委托关系”(Hide Delegate)
【29】移除中间人(Remove Middle Man)
【30】将双向关联改成单向关联(Change Bidirectional Association to Undirectional)
【31】以委托取代继承(Replace Inhheritance with Delegation
【32】提炼超类(Extract Superclass)
【33】引入外加函数(Introduce Foreign Method)
【34】引入本地扩展(Introduce Local Extension)
【35】封装字段(Encapsulate Field)
【36】封装集合(Encapsulate Collection)
【37】移除设值函数(Remove Setting Method)
【38】隐藏函数(Hide Method)
【39】函数下移(Push Down Method)
【40】字段下移(Push Down Filed)
【41】引入断言(Introduce Assertion)

1,重复代码(Duplicated Code):

  1,同一个类的两个函数含有相同的表达式
     方法:【1】 提炼函数(Extract Method)
            1,无局部变量
            2,有局部变量
  2,
     1> 两个互为兄弟的子类内含有相同的表达式
        方法:第一步:使用【1】
             第二步:【2】函数上移(pull up Method)
     2> 代码之间只是类似,并非完全相同
        方法:第一步:使用【1】将相似部分和差异部分分割开,构成单独的一个函数
             第二步:【3】塑造模板函数(Form Template Method)获得一个模板设计模式
             如果有些函数以不同的算法做相同的事,你可以选择其中一个,使用替换算法(Substitute Algorithm)将其他的函数替换掉
  3,两个毫不相关的类出现Duplicated Code
        方法:对其中一个使用 【4】 提炼类(Extract Class),将重复代码提炼到一个独立类中,然后在另一个类中使用这个新类。

2,过长函数(Long Method)

注:原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个函数中,并以其用途(而非实现方法)命名                  
  1,函数中有大量的参数和临时变量
     方法:【5】 以查询取代临时变量(Replace Temp with Query)来消除临时元素
  2,过长的参数列表
     方法:【6】 引入参数对象(Introduce parameter Object)和
          【7】 保持对象完整(Preserve Whole Object)
  3,如果以上都做了还是不行,杀手锏: 
     方法:【8】 以函数对象取代函数(Replace Method with Method Object)
  4,条件表达式
     方法:【9】 分解条件表达式(Decompose Conditional)
  5,循环
     方法:将循环和循环体内的代码提炼到一个独立的函数中

 注:怎样去确定应该提炼哪一段代码:*
     1,寻找注释
     2,条件表达式和循环

3,过大的类

  1,太多的实例变量
     方法: 【4】 将几个变量一起提炼至新类内。提炼时彼此相关的变量,将它们放在一起,
             通常如果类内的数个变量有着相同的前缀或字尾,这就意味着有机会把他们提炼到某个组件中,
             如果这个组件适合作为一个子类,使用 【10】 提炼子类(Extract Subclass)
  2,一个类拥有太多的代码
     方法: 【4】 和【10】,技巧:先确定客户端 如何使用他们,然后运用【11】 提炼接口(Extract Interface)为每一种使用方式提炼一个接口,这或许能让你看清楚如何分解这个类,
  3,GUI类
     方法: 将数据和行为移到一个独立的领域对象去,可能需要两边个保留一些重复数据,并保持两遍同步,【12】 复制“被监视数据”(Duplicate Observed Data)

4,过长参数列

  1,如果向一个已有的对象发出一条请求就可以取代一个参数,(已有的对象可能是函数所属类内的一个字段,也有可能是另一个参数)
     方法: 【13】 以函数取代参数(Replace parameter with Method)
  2,来自同一对象的一堆数据,以对象替换他们
     方法: 【7】
  3,某些数据缺乏很合理的对象归属
     方法: 【6】 为他们制造出一个对象
  4,不希望造成“被调用对象”与“较大对象”间的某种依赖关系,这时候将数据从对象中拆解出来单独作为参数,也是合理的

5,发散式变化(Divergent Change)

  1,某个类经常因为不同的原因在不同的方向上发生变化:比如:如果新加入一个数据库必须要修改这三个函数,如果新出现一种金融工具,必须要修改这四个函数
     方法: 将这个对象分成两个会很好,这么一来每个对象就可以只因一种变化而需改  【4】 

6,霰弹式修改(Shotgun Surgery)

  1, 如果每遇到某种变化,都必须在许多不同的类内做出许多小修改⑯
      方法:【14】 搬移函数(Move Method)和 【15】 搬移字段(Move Field)把所需修改的代码放进同一个类中,
           如果没有合适的类可以安置这些代码,那就创建一个,通常可以使用【16】 将类内联化(Inline Class)把一系列相关行为放进同一个类

7,依恋情结(Feature Envy)

  1,函数对某个类的兴趣高于对自己所处类的兴趣。比如:我们看到某个函数为了计算某个值,从另一个对象那调用几乎半打的取值函数。
     方法:把这个函数移至另一个地点,【14】把它移到它该去的地方,有时候函数只有一部分受这种依恋之苦,【1】 把这一部分提炼到独立函数中,再使用【14】把它带到他该去的地方。
     注:一个函数往往会用到几个类的功能,那么他究竟该被处置在哪?原则:判断哪个类拥有最多被此函数使用的珊瑚橘,然后就把这个函数和那些数据摆在一起。使用 【1】 将这个函数分解为数个较小的函数并分别置于不同的地方。

8,数据泥团(Data Clumps)

  1,常常可以在很多地方看到相同的三四个数据项:两个类中相同的字段、许多函数签名中相同的参数。
     方法:先找出这些数据以字段形式出现的地方,运用 【4】 将它们提炼到一个独立对象中,然后将注意力转移到函数签名上,运用【6】 或【7】 为他减肥,这么做的直接好处是将参数列表缩短,简化函数调用,你不必在意Data Clumps只用上新对象的一部分字段,只要以新对象取代两个(更多)字段。
     注:一个好的评判的办法:删掉众多数据中的一项。这么做,其他数据有没有因而失去意义?如果他们不再有意义,这就是一个明确的信号:你应该为他们产生一个新对象。

9,基本类型偏执(Primitive Obsession)

大多数编程环境都有两种数据:结构类型允许你将数据组织称有意义的形式,基本类型则是构成结构类型的积木块。结构总是会带来一定的额外开销,他们可能代表着数据库中的表,如果只为一两件事情而创建结构类型也可能显得太麻烦.对象的一个极大的价值在于:他们模糊(基本打破)了横亘于基本数据和体积较大的类之间的界限。
  1,对象技术的新手通常不愿意在小任务上运用一个大对象 ---像是结合数值呵呵币种的money类,由一个起始值和结束值组成的rang类、电话号码或邮政编码等的特殊字符串。
     方法:【17】以对象取代数据值(Replace Data Value with Object)将原本单独存在的数据值替换成对象。
  2,如果想要替换的数据值是类型码,而他并不影响行为,
     方法:则可以运用【18】以类取代类型码(Replace Type Code with Class)将它替换掉
  3,如果你有与类型码相关的条件表达式,
     方法:可运用【19】 以子类取代类型码(Replace Type Code with subclass)或 【20】 以State/Strategy取代类型码(Replace Type Code with State/Strategy)
  4,如果你有一组应该总是被放在一起的字段
     方法:【4】 
  5,如果你在参数列中看到基本类型数据
     方法: 【6】
  6,如果你发现自己正从数组中挑选数据           
     方法:【21】 以对象取代数组(Replace Array with Object)

10,Switch 惊悚现身(Switch Statements)

  1,以多态来取代他
     方法:使用 【1】 将switch语句提炼到一个独立的函数中,再以【14】 将它搬移到需要多态性的那个类里。此时,你必须决定是否使用 【19】 或【20】 ,一旦这样完成继承结构之后,可以运用 【22】 以多态取代条件表达式(Replace Conditional with Polymorphism)
  2,如果你只是在单一函数中有些选择事例 ,且不想改动他们,那么多态就有点杀鸡牛刀了
     方法:使用 【23】 以明确函数取代参数(Replace Parameter with Explicit Methods)
  3,如果你的选择条件之一是null
     方法:试试 【24】 引入Null对象(Introduce Null Object)

11,平行继承体系(Parallel Inheritance Hierarchies)

 是霰弹式修改(Shotgun Surgery)的特殊情况。
  1,每当你为某各类增加一个子类,必须也为另一个类相应增加一个子类,如果你发现某个继承体系的类名称前缀和另一个继承体系的类名称前缀完全相同
     方法:让一个继承体系的实例引用另一个继承体系的实例,再运用 【14】 和 【15】

12,冗赘类(Lazy Class)

  1,如果某各类没有做足够的工作
     方法:试试 【25】 折叠继承体系(Collapse Hierarchy),
  2,对于几乎没用的组件
     方法:你应该以 【16】 应对

13,夸夸其谈未来性(Speculative Generality)

  1,如果你的某个抽象类其实没有太大作用
     方法:请运用【25】
  2,不必要的委托
     方法:【16】
  3,如果函数的某些参数未被用上
     方法:【26】 移除参数(Remove Parameter)
  4,如果函数名称带有多余的抽象意味
     方法:【27】 函数改名(Rename Method)

14,令人迷惑的暂时字段

  1,其内某个实例变量仅为某种特定情况而定。
     方法:【4】  还可以使用 【24】  在“变量不合法”的情况下创建一个Null 对象,从而避免写出条件式代码
  2,如果类中有一个复杂的算法,需要好几个变量
     方法:【4】 把这些变量和其他相关函数提炼到一个独立类中,提炼后的新对象将是一个函数对象

15,过度耦合的消息链(Message Chains)

  1,实际代码中你看到的可能是一长串的getThis()或一长串的临时变量
     方法: 使用 【28】 隐藏“委托关系”(Hide Delegate),可以在消息链的不同位置进行这种重构手法
           更合适的做法 :先观察消息链最终得到的对象是用来干什么的,看看能否以 【1】 把使用该对象的代码提炼到一个独立函数中,在运用 【14】 把这个函数推入消息链,如果这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加一个函数来做这个事情

16,中间人(Middle Man)

对象的基本特征之一就是封装 --- 对外部世界隐藏其内部的实现细节。封装往往伴随着委托。
  1,过度的委托,你也许会看到某个类或接口有一半的函数都委托给其他类,
     方法:【29】 移除中间人(Remove  Middle Man),直接和真正负责的对象打交道,如果祝贺样“不干实事”的函数只有少数几个,可以运用 【16】 把他们放进调用端。如果Middle Man还有其他行为,可以运用 【30】以继承取代委托(Replace Delegation  with Inheritance)把它变成实责对象的子类,这样你即可以扩展对象的行为,又不用但系负担那么多的委托对象。

17,狎昵关系(Inappropriate Intimacy)

  1,两个类过于亲密,花费太多的时间去探究彼此的private 成分
     方法: 【14】 和【15】 帮他们划分界限,也可以运用 【30】将双向关联改成单向关联(Change Bidirectional Association to Undirectional)让类分开,如果两个类实在是情投意合,可以运用【4】 把两者共同点提炼到一个安全地点,让他们坦荡的使用这个新类。也可以尝试运用 【28】 让另一个类来为他们传递关系
  2,继承往往造成过度的亲密,因为子类对超类的了解总是超过后者的主观愿望,如果你觉得这个孩子可以独立生活了,运用 【31】 以委托取代继承(Replace Inhheritance with Delegation),让他离开继承体系。

18,异曲同工的类(Alternative Classes with Different Interfaces)

  1,如果两个函数做同一件事情,却有着不同的签名
     方法:【27】 根据他们的用途重新命名,但这往往不够,请反复运用 【14】 将某些行为移入类,直到两者的协议一致为止,如果你必须重复而赘余地移入代码才能完成这些,可运用 【32】 提炼超类(Extract Superclass)

19,不完美的库类

  1,如果你只想修改类库的一两个函数
     方法:可以运用【33】 引入外加函数(Introduce Foreign Method)
  2,如果想要添加一大堆额外行为
     方法:运用 【34】 引入本地扩展(Introduce Local Extension)

20,纯稚的数据类(Data Class)

他们拥有一些字段,以及用于访问(读写)这些字段的函数,除此以外一无长物。
  1,这种类只是一中数据容器,他们几乎一定被其他类过分细锁的控制着,这些类早起可能拥有public字段
     方法: 【35】 封装字段(Encapsulate Field)将他们封装起来。
  2,如果这些类内含容器类的字段
     方法:检查他是不是得到了恰当的封装:如果没有,就运用 【36】 封装集合(Encapsulate Collection)把他们封装起来。
  3,对于那些不该被其他类修改的字段
     方法:运用 【37】 移除设值函数(Remove Setting Method),然后找出这些取值/设值函数给其他类运用的地方。尝试以 【14】 把那些调用行为搬移到Data Class ,如果无法搬移整个函数,就运用 【1】 产生一个可被搬移的函数,不久之后,就可以运用 【38】 隐藏函数(Hide Method)把这些取值设值函数隐藏起来。

21,被拒绝的遗赠(Refused Bequest)

  1,子类应该继承超类的函数和数据,但如果他们不想或不需要继承,又该怎么办,他们得到所有礼物,却只从中挑选几样来完
     方法:你需要为这个子类新建一个兄弟类,再运用 【39】函数下移(Push Down Method)和 【40】 字段下移(Push Down Filed)把所有用不到的函数下推给那个兄弟,这样一来,超类就只持有所有子类共享的东西
  2,子类复用了超类的行为(实现),但又不愿意支持超类的接口
     方法: 【31】

22,过多的注释

  1,如果你需要注释来解释一段代码做了什么              
     方法: 【1】 
  2,如果函数已经提取出来了,但还是需要注释来解释其行为 
     方法:【27】
  3,如果你需要注释说明某些系统的需求规格
     方法: 【41】 引入断言(Introduce Assertion)

你可能感兴趣的:(java,代码优化,重构,java)