• 《重构》学习总结


    前段时间我学习了《重构》这本书,在学习过程中,我就每个重构方法的目的就自己的理解做了一些总结,如下:


    1      重新组织函数

    1.1 提炼函数(Extract Method)

    解释:如果一个函数过于复杂,就把它拆分为多个职责清晰的小函数。

    目的:1、复杂的函数难于理解。

    2、拆分成小函数后能够提高小函数的复用度。

    1.2 内联函数(Inline Method)

    解释:如果一个函数过于简单,就把它取消,并把它的代码内联到调用它的地方。

    目的:1、降低过度的委托引入的间接性。

    2、作为其他方法的前提,采用这种方法整理之后形成的大函数,可以再用其他方法进行整理。

    1.3 内联临时变量(Inline Temp)

    解释:如果临时变量的表达式比较简单,可以取消函数临时变量,把临时变量的表达式置换到使用临时变量的地方。

    目的:一般作为其他方法的前提使用。

    1.4 以查询取代临时变量(Replace Temp with Query)

    解释:把临时变量表达式提取到一个独立函数,并在引用临时变量的地方调用。

    目的:1、能够使得表达式在整个类空间可见,避免函数越写越长。

    2、作为其他方法的前提使用。

    总结:1、如果查询函数复杂,多次调用这个查询函数可能会降低程序性能。

    2、这个方法看起来也别扭。

    1.5 引入解释性变量(Introduce ExplainingVariable)

    解释:如果表达式过于复杂,可以增加临时变量来缓存表达式的值。

    目的:1、降低复杂表达式带来的程序难以理解性。

    1.6 分解临时变量(Split Temporary Variable)

    解释:如果临时变量被赋值超过一次,应该针对每次赋值创造独立、对应的临时变量。

    目的:临时被赋值多次,说明它承担了多个职责,会使代码阅读者迷惑。

    总结:纯代码美学方法。

    1.7 移除对入参的赋值(Remove Assignments toParameters)

    解释:如果对函数入参直接赋值或操作,应该引入临时变量,先对临时变量赋值或操作,最后根据入参是否需要返回来决定是否把临时变量赋回给入参。

    目的:主要为了提高代码清晰度。

    总结:纯代码美学方法。

    1.8 以函数对象取代函数(Replace Method with MethodObject)

    解释:如果复杂函数中局部变量使用太多,不便提炼出小函数,可以将复杂函数变成一个对象,之前的局部变量就变成这个对象的数据成员,从而使复杂函数提炼出小函数避免受传参的影响。

    目的:为了在局部变量使用太多难以提炼小函数的情况下提炼小函数,提高代码复用度和清晰度。

    1.9 替换算法(Substitute Algorithm)

    解释:把函数用更简单的算法实现。

    目的:优化原有函数实现方法。

    总结:抽象意义上的重构方法。

    2      在对象之间搬移特性

    2.1 搬移函数(Move Method)

    解释:如果一个类成员函数被其他类使用或者使用其他类,超过使用自身类或被自身类使用,就应该将这个函数搬移到那个类中去。

    目的:降低类之间的耦合,提高内聚,使类的职责更清晰。

    2.2 搬移字段(Move Field)

    解释:如果一个类数据成员被其他类使用超过被自身类使用,就应该将这个数据成员搬移到那个类中。

    目的:降低类之间的耦合,提高内聚,使类的职责更清晰。

    2.3 提炼类(Extract Class)

    解释:如果一个类太庞大且有多项职责,可以把各项职责拆分成不同的类。

    目的:使类的职责更清晰,容易维护。

    2.4 将类内联化(Inline Class)

    解释:如果一个类不能单独承担责任,可以把这个类合并入调用它最频繁的类中去。

    目的:使类的职责更清晰,容易维护。

    2.5 隐藏“委托关系”(Hide Delegate)

    解释:客户不直接访问被委托类,而通过服务类来访问被委托类。

    目的:服务类保持对客户的接口稳定,被委托类的变化被封装服务类范围内,减少类之间的耦合。

    总结:在设计模式中大量可见,如组合模式、外观模式、代理模式等。

    2.6 移除中间人(Remove Middle Man)

    解释:如果服务类完全是只做委托工作时,那么可以把服务类去掉,客户直接访问被委托类。

    目的:过多的委托会引入复杂性,这个方法有利于在过度委托时简化代码。

    2.7 引入外加函数(Introduce Foreign Method)

    解释:如果在服务类扩展一个成员函数不方便的时候,只能在客户端扩展这个函数。

    目的:在服务类不开源或无法修改的情况,面对有扩展函数需求时的一种折中做法。

    2.8 引入本地扩展(Introduce Local Extension)

    解释:如果服务类需要扩展较多的成员函数,且不方便在服务类添加的时候,可以为服务类定义个外覆类或者定义子类继承与服务类。

    目的:在服务类不开源或无法修改的情况,扩展较多的函数接口,并保持类的封装性。

    总结:在设计模式中可见,如外观模式、装饰模式等。

    3      重新组织数据

    3.1 自封装字段(Self Encapsulate Field)

    解释:为类中的数据成员提供set/get函数,通过这些函数来读写数据成员。

    目的:通过set/get函数间接访问可以支持灵活的数据管理方式,比如懒初始化。

    总结:一般不做这类封装。

    3.2 以对象取代数据值(Replace Data Value withObject

    解释:当围绕着数据的相关操作逐步增多时,可以把这个数据和相关操作抽出来变成独立的类。

    目的:符合面向对象职责独立的原则,提高代码复用度。

    3.3 将值对象改为引用对象(Change Value to Reference

    解释:把类中一个实值数据成员对象改为一个引用数据成员对象。

    目的:1、如果希望类的多个对象共享一个数据成员对象,就可以把实值改为引用。

    2、通常改成引用的这个数据成员对象属于数值类型的。

    3.4 将引用对象改为值对象(Change Reference to Value)

    解释:把类中一个引用数据成员对象改为一个实值数据成员对象。

    目的:1、在并发系统中实值对象不用考虑同步问题。

    2、这个数据成员对象通常是不可变的。

    3.5 以对象取代数组(Replace Array with Object)

    解释:用对象来替换数组,对于数组中每个元素,以对象的一个值域来表示。

    目的:主要为了容易记忆数组中各元素的含义。

    总结:代码美观整理类方法。

    3.6 复制“被监视数据”(Duplicate Observed Data)

    解释:对于业务处理和界面显示分层的软件结构,可以在业务模块和界面模块分别存储数据,并在业务模块数据改变时能够通知界面模块更新数据。

    目的:便于软件分层,各层便于独立演化,特别是可以采用多样化的界面模块来显示业务模块。

    总结:属于观察者模式,MFC的文档视图结构也是这种情况。

    3.7 将单向关联改为双向关联(Change UnidirectionalAssociation to Bidirectional)

    解释:把两个类之间的单向引用变成双向引用。

    目的:主要根据业务的需要使用,同时要确定哪个类是主控类。

    3.8 将双向关联改为单向关联(Change BidirectionalAssociation to Unidirectional)

    解释:把两个类之间的双向引用变成单向引用。

    目的:降低类之间的耦合性,减少出错机会。

    3.9 以字面常量取代魔法数(Replace Magic Number withSymbolic Constant)

    解释:把程序中的魔法数用常量取代。

    目的:1、便于今后修改魔法数时不用到程序每个地方都修改。

    2、让魔法数更有意义。

    3、当由于魔法数出现问题时,由于常量在程序中保有符号信息,便于定位问题。

    3.10        封装字段(Encapsulate Field)

    解释:把类的公共数据成员变成私有数据成员,并提供set/get函数来读写数据成员。

    目的:1、提高类的封装性。

    2、当需要修改数据成员读写时,能将修改集中于set/get函数中。

    3.11        封装集合(Encapsulate Collection)

    解释:当一个类有容器数据成员时,用户对容器元素的访问应该通过类的接口直接进行,而不是通过返回容器数据成员,再通过容器数据成员的接口访问容器元素。

    目的:向用户屏蔽容器数据成员的存在,提高封装性,降低客户与类的耦合性。

    3.12        以数据类取代记录(Replace Record with Data Class)

    解释:如果一个类有记录类型数据成员变量,可以把这个数据类成员变量来替代。

    目的:有利于面向对象思想体现,提高封装性。

    3.13        以类取代类型码(Replace Type Code with Class)

    解释:把通常用enum表示的类型码用类来取代。

    目的:当类型码作为函数入参时,改成类后,有利于使用静态语言的编译器类型检查,减少出错机会。

    总结:会引入额外的复杂度。

    3.14        以子类取代类型码(Replace Type Code with Subclasses)

    解释:如果一个类会针对类型码有不同的操作处理,可以针对每个类型码定义一个派生类,把类型码特定的处理放到派生类中去。

    目的:1、更好的利用多态的思想来处理变化,方便后续扩展新的类型码相关处理。

    2、适用于类型码在对象创建后不变的情况,以及类型码的宿主类还没有其他派生类的情况。

    总结:会引入额外的复杂度。

    3.15        以State/Strategy取代类型码(Replace Type Code with State/Strategy)

    解释:如果一个类会针对类型码有不同的操作处理,且不方便使用上述重构方法时,可以定义一个可派生的类成员对象,把类型码特定的处理放到这个成员对象中去。

    目的:1、如果类型码在类对象创建后会变化,类与成员对象之间关系可以定义为状态模式。

    2、如果只是类已有子类不方便再定义子类,类与成员对象之间关系可以定义为策略模式。

    总结:会引入额外的复杂度。

    3.16        以字段取代子类(Replace Subclass with Fields)

    解释:如果子类之间的差异只包含有常量值,那么可以把子类删除,把常量值变成父类一个数据成员。

    目的:在类存在没有价值时,减少类的数量,把子类合并入父类,避免因为类数量多造成的复杂性。

    4      简化条件表达式

    4.1 分解条件表达式(Decompose Conditional)

    解释:把条件表达式的各部分分别提炼成函数。

    目的:突出条件逻辑,提高可读性

    总结:纯代码美学方法。

    4.2 合并条件表达式(Consolidate ConditionalExpression)

    解释:如果一系列条件式有相同的结果,就可以将这些条件式提炼成一个独立函数。

    目的:提高代码清晰可读性。

    总结:代码整理方法。

    4.3 合并重复的条件片段(Consolidate DuplicateConditional Fragments)

    解释:如果条件式的各分支有一段相同的代码,可以把这段相同的代码搬到条件式外。

    目的:提高代码清晰可读性。

    总结:代码整理方法。

    4.4 移除控制标记(Remove Control Flag)

    解释:如果表达式带有控制标记,那么可以用break或return来取代控制标记。

    目的:提高代码清晰可读性。

    总结:代码整理方法。

    4.5 以卫语句取代嵌套条件表达式

    解释:如果条件表达式逻辑里只有一种情况是正常的,其他情况都是不正常的,可以用卫语句来检查并处理返回不常见的情况。

    卫语句示例:if(capital<0)return 0;

    目的:整理代码,提高代码清晰性。

    4.6 以多态取代条件表达式(Replace Conditional withPolymorphism)

    解释:如果条件表达式是根据类型不同选择不同的行为,那么可以构建一个多态体系来替代条件表达式。

    目的:采用面向对象的思想,利用多态机制减少switch语句使用。

    总结:暂时还没体会到这种方法的好处,类似3.14、3.15。

    4.7 引入Null对象(Introduce Null Object)

    解释:对于类对象为空或者为比较异常的情况下,构造一个空对象或异常对象,可以减少处理空或异常情况的代码。

    目的:遵循面向对象的思想,利用多态机制减少异常处理代码,精简程序。

    总结:暂时还没体会到这种方法的好处。

    4.8 引入断言

    解释:对于程序状态的判断可以采用断言取代表达式。

    目的:1、能够避免程序在出错的状态下继续运行下去。

    2、能在更接近错误发生地点把错误检测出来。

    5      简化函数调用

    5.1 函数改名(Rename Method)

    解释:修改函数名字,让名字能较好体现函数的意图。

    目的:提高代码可读性,避免误解。

    5.2 添加参数(Add Parameter)

    解释:给函数添加参数。

    目的:根据代码业务需要添加,尽量控制函数入参数量。

    5.3 移除参数(Remove Pamameter)

    解释:把函数不使用的参数移除。

    目的:根据代码业务情况移除,尽量控制函数入参数量。

    5.4 将查询函数和修改函数分离(Separate Query fromModifier)

    解释:如果一个函数既返回对象状态,又修改对象状态,就要拆分成两个函数,一个查询函数返回对象状态,另一个修改函数修改对象状态。

    目的:1、代码美学方法。

    2、拆分函数有利于提高代码复用度。

    5.5 令函数携带参数(Parameterize Method)

    解释:如果多个函数做了类似的动作,只有少数几个值导致少量动作不同,可以建立一个单独函数,通过入参来控制多个函数之间的动作差异。

    目的:提高代码复用度。

    5.6 以明确函数取代参数(Replace Parameter withExplicit Methods)

    解释:如果一个函数根据参数值不同而采取完全不同的行为,可以针对该参数的每个可能值,分别建立独立函数。

    目的:1、提高代码清晰度。

    2、是5.5的反向方法,区别在与参数值影响函数行为是否是少量还是大量,如果是少量就该采用5.5的方法,如果是大量就该采用本方法。

    5.7 保持对象完整(Preserve Whole Object)

    解释:如果一个函数的入参需要一个对象的若干成员值,可以改成将那个对象整个传入函数。

    目的:1、减少函数入参数量,稳定函数接口。

    2、负面作用提高了函数与对象的耦合关系。

    5.8 以函数取代参数(Replace Parameter withMethods)

    解释:如果一个函数的入参是通过调用另一个函数获得的,那么可以移除这个入参,通过在函数里调用那一个函数来获取。

    目的:减少函数入参数量。

    5.9 引入参数对象(Introduce Parameter Object)

    解释:如果若干函数都有相同一组入参,可以把这组入参封装成对象,然后传递给这些函数。

    目的:减少函数入参数量,增加封装性。

    5.10        移除设值函数(Remove Setting Method)

    解释:如果一个类的成员变量不会被改变,就移除这个变量相关的设置函数。

    目的:减少这个成员变量被误改的机会。

    5.11        隐藏函数(Hide Method)

    解释:如果类的一个成员函数没有被用到,就把它设成私有的。

    目的:提高隐藏性,减少暴露接口。

    5.12        以工厂函数取代构造函数(Replace Constructor with Factory Method)

    解释:如果构建对象时不仅仅对它做简单的构建动作,就把构造改变成为工厂函数。

    目的:1、减少客户需要了解到的对像创建行为细节。

    2、可以在创建对象时用工厂函数接口取代类型码。

    总结:工厂模式。

    5.13        封装向下转型(Encapsulate Downcast)

    解释:如果函数返回的对象,需要向派生类转型,则需要把这个转型放到函数内。

    目的:提高代码清晰度,减少出错机会。

    5.14        以异常取代错误码(Replace Error Code with Exception)

    解释:函数出错时,把返回错误码改为抛出异常。

    目的:当出错的地方不知如何处理错误时,能够沿函数调用链传递异常,直到找到能够处理这种错误的地方。

    总结:C++标准异常机制。

    5.15        以测试取代异常(Replace Exception with Test)

    解释:如果对于一个函数的条件检查,应该使用测试语句而不是抛出一个异常。

    目的:对于检查而不是出错的情况下,不要滥用异常机制。

    6      处理概括关系

    6.1 字段上移(Pull Up Field)

    解释:如果两个子类有相同的成员变量,把这个成员变量移到父类。

    目的:提高复用度。

    6.2 函数上移(Pull Up Method)

    解释:如果两个子类有基本相同的成员函数,把这个成员函数移到父类。

    目的:提高复用度。

    6.3 构造函数本体上移(Pull Up Constructor Body)

    解释:如果两个子类有基本相同的构造函数,可以在父类定义这个构造函数,然后各个子类的构造函数分别调用父类的构造函数。

    目的:提高复用度。

    6.4 函数下移(Pull Down Method)

    解释:当父类的函数只与部分子类有关时,可以把父类函数移到有关的子类中去。

    目的:根据程序需要定义函数存在位置。

    6.5 字段下移(Pull Down Field)

    解释:当父类的成员变量只与部分子类有关时,可以把父类成员变量移到有关的子类中去。

    目的:根据程序需要定义成员变量存在位置。

    6.6 提炼子类(Extract Subclass)

    解释:如果类的某些特性只被部分实例使用到,可以把这些特性移到子类中去。

    目的:重构类的继承体系,使之更合理,也可以在继承和委托之间做取舍。

    6.7 提炼超类(Extract Superclass)

    解释:如果两个类有相似特性,可以提炼出一个父类出来。

    目的:重构类的继承体系,使之更合理,也可以在继承和委托之间做取舍。

    6.8 提炼接口(Extract Interface)

    解释:如果多个类的接口有子集,那么可以把接口提炼出来。

    目的:符合面向接口编程的思想。

    总结:Java语言才有接口。

    6.9 折叠继承体系(Collapse Hierarchy)

    解释:如果子类和父类之间区别不大,可以把它们合二为一。

    目的:重构类的继承体系,使之更合理。

    6.10        塑造模板函数(Form Template Method)

    解释:如果多个子类有些函数执行动作大体相同,可以把这个函数移到父类,然后对于具体不同之处定义虚函数,让各子类实现这些虚函数以实现不同那些不同之处。

    目的:提高代码复用程度。

    总结:模板方法。

    6.11        以委托取代继承(Replace Inheritance with Delegation)

    解释:如果子类只适用父类一部分接口,那么可以取消继承关系,改成委托关系,使子类保存父类对象的句柄,通过句柄访问父类接口。

    目的:重构类的继承体系,使之更合理。

    6.12        以继承取代委托(Replace Delegation with Inheritance)

    解释:如果两个委托关系的类,一个类使用了另一个类的所有接口时,那么可以取消两个类的委托关系,改成继承关系。

    目的:1、重构类的继承体系,使之更合理。

    2、如果一个类没有使用另一个类所有接口,这个方法不适用,可以采用提炼超类方法。

    3、如果多个对象使用被委托对象,或者被委托对像是可变的,这方法也不适用。

    7      大型重构

    7.1 梳理并分解继承体系(Tease Apart Inheritance)

    解释:如果一个继承体系同时承担两项职责,可以建立两个继承体系,通过委托,让其中一个调用另一个。

    目的:使得继承体系的职责独立,方便扩展。

    7.2 将过程化转化为对象设计(Convert ProceduralDesign to Objects)

    解释:对于过程化风格写成的程序,可以转变成面向对象风格,把数据变成对象,把行为分开,并将行为移入对象中。

    目的:使用面向对象程序设计风格。

    7.3 将领域和表述/显示分离(Separate Domain fromPresentation)

    解释:如果表示类里包含业务逻辑,可以将业务逻辑分离开来,形成独立的业务逻辑类。

    目的:视图与逻辑相分离,便于扩展。

    总结:观察者模式。

    7.4 提炼继承体系(Extract Hierarchy)

    解释:如果一个类承担了较多的工作,并且通过条件式完成的,可以建立一个继承体系,用一个子类表示条件式的一种情况。

    目的:整理类继承体系,使得职责清晰独立,方便扩展。

  • 相关阅读:
    近来无聊 写了一个google音乐的下载器
    HTML编辑器 HtmlArea
    Html编辑器 TinyMCE
    IE浏览器自定义地址协议 通过B/S程序中打开C/S程序
    html 元素的 onpropertychange 事件
    asp.net 服务器控件防止重复提交表单
    用 BindingSource 绑定窗体中控件不失去焦点无法更新数据源的问题
    动态创建大量 html dom 元素时提升性能
    很黄的日期格式化。。。
    Asp.net 导出 .html,.txt 等格式的文件
  • 原文地址:https://www.cnblogs.com/james1207/p/3303942.html
Copyright © 2020-2023  润新知