zoukankan      html  css  js  c++  java
  • 代码重构的常见症状与重构方法

        最近在看《重构》一书,收获颇多。

        重构,是有迹可循的。某些模式的代码,向我们昭示着重构的可能,书中作者称之为“代码的坏味道”。

        一:重复的代码

        在程序中出现两次以上的程序结构,应该进行重构:

        1:在一个函数中出现重复的结构(如:多个if语句),就要考虑优化算法,使用更简洁、高效的写法。

        2:同一个类中出现两次以上相同结构的代码,则提取出来作为一个函数。

        3:两个互为兄弟的子类之间含相同代码,则先提取出来作为独立函数,然后上推到父类中。

        4:两个互为兄弟的子类之间含相似代码,则先把相同部分提取作为函数1,不同部分作为函数2,然后在函数3中调用函数1、函数2,并把相同签名的函数上推到父类。【这样,由于子类各自重写了函数1、2,那么子类的函数3产生的结果就不同。此法名为“塑造模版函数”】

        5:两个毫不相关的类中重复代码,则把重复代码提取到一个新的独立类中,然后在原来的类中通过新类进行调用。(例如:提取到工具类中)

        二:过长函数

        函数是功能的基本单元,一个函数一个尽量承担一个职责。如果一个函数中,做了多步工作,则应该进行重构:

        1:我们在编写函数时,如果需要用注释来说明某一块代码时,则应该优先考虑把这部分代码作为一个函数来定义,并且通过函数名来说明其用途;在重构长函数时,这也是特征之一 —— “函数中哪里需要用注释说明其用途,则尝试提取出来作为独立函数,用函数名表达其用途

        2:对已有长函数进行分解:以单一功能为指标,提取每一部分代码进独立函数,最后原函数只需通过一系列调用语句,引用被提取出去的函数即可。

        3:长函数中的临时变量:在原函数中,如有使用一些临时变量来接收某个函数调用结果的,则把这些临时变量直接用函数调用语句代替。

        4:过长函数参数列:过长函数参数列表是函数调用出错的主要原因,可以新建一个参数类,把参数作为类成员,而调用时只需传递一个参数类对象即可。

        5:有太多临时变量和参数不能替代或提取:使用函数对象法:新建一个新的类,在其中通过一个成员变量,引用原来的类;把原函数中用到的临时变量、参数,全部作为类成员字段;定义一个函数,通过使用成员字段,实现与原函数一样的功能;最后,将原函数改造为:新建函数功能类对象(this,原临时变量,参数 作为构造参数),调用功能函数,并把结果返回。

        6:条件表达式改造:用于if语句的判断表达式往往是造成代码可读性下降的原因之一,某些判断语句用到的数据需要通读上下文才能理解。可以将条件语句提取出来,作为一个独立函数,通过函数名表达其判断内容,而函数內根据判断语句返回true 或 false 即可。

        7:循环语句改造:循环语句块同样可以承担单一职责,因此可以提取出来作为独立函数,函数名表达其用途。

        三:过大的类

        类是面向对象而设计的,如果一个类中包含了太多与其本身无关的功能时,就要考虑重构:

        1:将与本类无关的变量、函数,提取出来作为一个新类;

        2:如果提取出来的变量、函数,适合作为一个子类,则使用提取子类法;

        

        四:过长参数列表

        1:如果参数是某个函数的调用结果,则直接使用函数调用语句作为参数;

        2:如果某几个参数是属于某一个类的字段,则使用该类的对象作为参数,以保持对象的完整性;

        3:剩下的杂乱无章、缺乏归属对象的参数,则为它们制造一个类,用以容纳这些参数,以参数类对象作为函数参数。

        五:发散式变化【一个类受多种变化影响】

        类的设计要有可扩展性,并且修改要容易进行。如果一个类需要引入不同变化时,对于每种变化,需要修改多个地方,则需要进行重构:

        以变化为基本单元,对于某一种变化,所引起的修改,提取到一个新的类中,使得每种变化都分别对应于一个类而进行。

        六:霰弹式变化【一个变化,影响多个类】

        如果有一种变化,需要在多个不同类中进行修改,则需要进行重构:

        根据这个变化所引起的修改,把它们全部提取到一个类中。

        七:放错位置

        如果一个类中,有函数对另一个类的内容调用颇多,则需要进行重构:

        1:如果是一个函数过多调用另一个类的数据,则把该函数“搬移”到被那个类去;

        2:如果是一个函数中部分代码过多调用另一个类中数据,则先把该部分代码提炼为独立函数,然后再搬移。

        八:零散数据

        如果有一些数据,常常在一起出现,例如:作为函数参数经常出现,则需要进行重构:

        把这些零散的数据提炼到一个新的类中,以对象为单位来组织、使用这些数据。

        九:替换基本数据类型

        对于某些小规模、少字段的信息,虽然可以用几个基本数据类型来表达出来,但是这些零散数据一旦分开使用,就让人摸不清用途。所以需要重构:

        1:用小对象组合零散数据:例如:带有数值与货币种类的money类、由start和end字段组成的range类等,类名清晰易懂。

        2:不影响类行为,只用于表示某内容的类型码替换:

        3:影响类行为的类型码替换: 

        

        十:switch语句块重构

        将switch语句提炼到独立函数,尽量用多态来取代case判断语句的基本数据类型,最后把该函数上推到父类中去。

        十一:平行继承

        如果有两个继承体系,体系一的子类用途与体系二的子类用途相似,则需要重构:

        在体系一中引用体系二子类实例,将原子类中的函数改写为调用体系二子类实例中函数。

        十二:冗余类

        如果有一些子类、独立类,没有承担明确的用途,那么就需要重构:

        1:如果是没明确职责的子类,则折叠继承体系——把子类内容合并到父类去,取消子类。

        2:如果是没明确职责的独立类,则将其内容合并到最频繁调用它的类中去。

        十三:过长的调用链

        如果存在一个类调用类2,类2调用类3...造成一长串调用关系,则需要重构:

        1:隐藏委托关系:把 类1对象.类2对象字段.getXX() 形式的代码,在类1中进行封装,定义  get类2XX()  函数,在函数中通过类2对象.getXX()调用,并把结果返回。

        

        十四:去掉中间人

        1:  过度委托(一类中超过一半方法需要靠委托类来调用其他类方法):则隐藏中间人,去掉委托类,让调用者直接与负责的对象打交道。

        2:如果只有少数函数需要委托类来调用其他类方法:则把这些函数放进调用端,直接用调用端.XX()调用即可。

        3:如果委托类还有其他行为,则使用“继承取代委托”,继承实际负责类作为子类,从而扩展原对象的行为,又可以调用原对象的方法。

        十五:访问私有

        如果两个类之间彼此过多访问private内容,则需要重构:

        1:把经常需要互相调用的内容提取到一个新的类中,在新类中光明正大地直接调用;

        2:子类可以独立成类:则用委托取代继承。将子类作为一个独立的类来定义,在其中使用一个原父类对象进行内容调用。

        十六:异曲同工的内容

        如果有功能相同的函数、代码,则将它们进行统一。

        十七:为原有类库添加函数

        如果需要在原有的类库基础上添加新函数,可以使用继承原类库的功能类,在子类中添加新函数,在程序中使用自定义的子类即可。

        十八:类中的字段封装

        类中的字段应该保持私密性:

        1:普通字段封装:将public改为private,并定义public修饰的setter/getter函数。

        2:集合字段封装:对于集合类型的字段,定义public修饰的remove/add函数,在函数中通过集合字段本身调用removeadd操作。

        3:只读字段:对于一些定义了之后就不再修改的字段,我们应该在类的构造函数中进行赋值,然后只提供getter函数,隐藏setter函数。

        

        十九:拒绝继承

        如果一个子类只需要父类中的少数内容,那就应该用委托取代继承,避免在子类中无谓地实现父类的接口。

        

        二十:注释过多

        注释可以增强代码可读性,但是注释也为我们指明了重构的方向。

        1:需要用注释来说明一个代码块的用途时,尝试将其提取为独立函数,用函数名来表达用途;

        2:需要注释来说明某种条件、状态时,使用断言。

        

  • 相关阅读:
    Django-session+CBV+ORM应用
    Django-session实现登陆
    Django-ORM-操作
    事件委托
    数组去重的方法
    闭包
    Javascript中继承
    函数调用的方式
    原型链的理解
    jsonp
  • 原文地址:https://www.cnblogs.com/ygj0930/p/7803868.html
Copyright © 2011-2022 走看看