• 《重构改善代码设计》


    文章目录

    1.重构的原则

    在这里插入图片描述

    2.代码的坏味道

    1. 神秘命名:不能清晰表述含义和功能的命名。
      重构手法:改变函数命名、变量改名、字段改名
    2. 重复代码:一个以上地方出现相同的代码结构
      重构手法: 提炼函数,移动语句、函数上移
    3. 函数过长:需要注释来解释的时候,就要考虑是否需要拆解函数;条件表达式也是拆解函数的信号。
      重构手法:提炼函数、以查询取代临时变量、引入参数对象、保持对象完整、以命令取代函数、分解条件表达式、以多态取代条件表达式、拆分循环。
    4. 参数列表过长:从一个现有的数据结构中获取多个值时,可以直接使用数据结构;多个函数有同样的参数,可以将函数组合成类,把函数参数作为类的属性。
      重构手法:以查询取代参数、保持对象完整、引入参数对象、移除标记参数、函数组合成类。
    5. 全局数据:可以在代码的任何位置进行修改,带来隐患。
      重构手法:封装变量
    6. 可变数据:随着变量作用域的扩展,会使风险变大
      重构手法:封装变量、拆封变量、移动语句、提炼函数、将查询函数和修改函数分离、移除设值函数、以查询取代派生变量、函数组合成类、函数组合成变换、将引用对象改为值对象。
    7. 发散式变换:如果某个模块经常因为不同的原因在不同的方向上发生变换;增加某个功能,需要修改很多处。
      重构手法:提炼函数、搬移函数、提炼类、拆分阶段。
    8. 散弹式修改:每遇到某种变化,需要在许多不同的类内做许多小的修改
      重构手法:搬移函数、搬移字段、函数组合成类、函数组合成变换、拆分阶段、内联函数、内联类。
    9. 依恋情结:模块化即是力求将代码分出区域,最大化区域内部交互,最小化跨区域交互;一个函数跟另一个模块中的函数或者数据交流格外频繁,远胜于在自己所处模块内部的交流;将总是一起变化的东西放在一块儿。
      重构手法:搬移函数、提炼函数
    10. 数据泥团: 多个类中相同的字段,函数中相同参数,绑定在一起出现的参数。
      重构手法:提炼类、引入参数对象、保持对象完整
    11. 基本类型偏执:不要过分依赖基础类型,合适的时候封装为对象。
      重构手法:以对象取代基本类型、以子类取代类型码、以多态取代条件表达式、提炼类、引入参数对象
    12. 重复的switch:强调重复的switch,维护时必须找到所有的switch语句。
      重构手法:以多态取代条件表达式
    13. 用管道取代循环语句
    14. 冗赘的元素:过于简单的结构、类或函数可以直接内联掉不需要在单独封装
      重构手法:内联函数、内联类、折叠继承体系
    15. 过度设计:不要为了未来的一些事情而使当前的代码进行特殊处理
      重构手法:折叠继承体系、内联函数、内联类、改变函数声明、移除死代码
    16. 临时变量:类内部某个字段仅为某特定情况而设置
      重构手法:提炼类、搬移函数、引入特例
    17. 过长的消息链:跨多层对象取值即是过长的消息链。
      重构手法:隐藏委托关系、提炼函数、搬移函数
    18. 过度委托:将一个类的大部分函数都委托给其他类,此时可移除中间类
      重构手法:移除中间人、内联函数、以委托取代基类、以委托取代子类
    19. 内幕交易:模块间进行大量的数据交互,应尽量减少。
      重构手法:搬移函数、搬移字段、隐藏委托关系、以委托取代基类、以委托取代基类
    20. 类过大:不要利用一个类做过多的事情,要遵循单一职责
      重构手法:提炼类、提炼基类、以子类取代类型码
    21. 异曲同工类:接口不一致的类无法互相替换。
      重构手法:改变函数声明、搬移函数、提炼基类
    22. 纯数据类:纯数据类即拥有一些字段,以及用于访问字段的函数,避免出现public字段。
      重构手法:封装记录、移除设值函数、搬移函数、提炼函数、拆分阶段
    23. 被拒绝的遗赠:基类中不应该有子类不想或不应该继承的数据和函数。
      重构手法:函数下移、字段下移、以委托取代子类、以委托取代基类
    24. 注释:当出现需要注释解释的代码,则表示需要通过提炼函数来解释其行为。
      重构手法:提炼函数、搬移函数、引入断言

    3.第一组重构

    3.1.提炼函数

    目的: 如果一段代码需要浏览全部代码才能知道它的功能,那么就应该提炼函数,以函数实现功能进行命名。
    实现:
    a.创造新函数并以功能进行命名,以做什么来命名,而不是怎么做来命名.
    b.将待提炼代码拷贝到新函数中,并检查是否引用了作用域以外的函数或变量,如果有则以参数方式传入新函数。
    c.在源函数中替换为新函数。

    3.2.内联函数

    目的:
    a.内部函数代码和函数名一样清晰易读,就没有必要封装为函数
    b.多个组织不合理的函数,可以将其合并为大函数再重新提炼。
    实现:
    a.检查函数,确定其不具备多态
    b.找出函数所有调用点,替换为函数本体
    c.删除函数定义

    3.3.提炼变量

    目的: 表达式复杂难读时,局部变量可以帮助将表达式分解为比较容易管理的形式
    实现:
    a.确定要提炼的表达式没有副作用
    b.声明不可变变量,复制要提炼的表达式使其值赋值给新变量
    c.用新变量取代表达式

    3.4.内联变量

    目的: 表达式比变量表达意思更清晰时,内联变量
    实现:
    a.确定变量右侧表达式没有副作用
    b.将使用变量的地方替换为表达式
    c.替换所所有使用变量的地方
    d.删除变量的声明和赋值语句

    3.5.修改函数名称

    目的: 好的函数名,可以直接通过函数名看出函数的功能,不需要查看代码的实现
    实现:
    简单实现:
    a.想要移除参数,需要确认函数体内没有使用该参数
    b.修改函数名字
    c.找到所有旧函数将其替换
    迁移式实现:
    a.有必要的化,先重构函数内部,方便后续步骤展开
    b.使用提炼函数,将函数提炼成新函数
    c.如果需要添加参数,采用简单做法添加
    d.对旧函数使用内联函数

    3.6.封装变量

    目的:
    a.提供一个清晰的观测点,由此监控数据的变化和使用
    b.可以轻松添加数据被修改时的验证和后续逻辑
    c.缩小变量的可见范围
    实现:
    a.创建封装函数,在其中访问和更新变量值
    b.修改所有使用该变量的地方,使其调用封装函数
    c.限制变量的可见性

    3.7.变量改名

    目的: 好的变量名可以解释一段程序在做什么
    实现:
    a.如果变量被广泛使用,考虑运用封装变量将其封装
    b.修改所有使用该变量的地方

    3.8.引入参数对象

    目的: 一组数据结伴同行,多处被使用,可将其封装为数据结构
    实现:
    a.如果没有合适的数据结构,就创建一个
    b.使用修改函数名称给原函数新增一个新建的数据结构作为参数
    c.修改所有的调用处,使用新的数据结构
    d.用数据结构中的元素,替换参数列表中的参数项,删除原来的参数

    3.9.函数组合成类

    目的: 一组函数形影不离的操作同一块数据,通常是将这块数据作为参数传递给函数
    实现:
    a.运用封装基类对多个函数公用的数据基类加以封装
    b.对于使用该基类结构的每个函数,运用搬移函数将其移新类
    c.用以处理该数据记录的逻辑可以用提炼函数提炼出来,并移入新类

    3.10.函数组合成变换

    目的: 有多个地方对关联的一组数据进行计算处理,可以将其合并到一起共同处理。以减少对关联数据做相似的处理,来减少代码重复。
    实现:
    a.创建一个变换函数,输入参数是需要变换的记录,并直接返回该记录的值
    b.挑选一块逻辑,将其主体移入变换函数中,把结果作为字段添加到输出记录中,修改客户端代码,令其使用新字段
    c.针对其他的相关计算逻辑,重复上述步骤

    3.11.拆分阶段

    目的: 一段代码在处理超过一件不同的事情时,需要拆分
    实现:
    a.将第二阶段的代码提炼成独立的函数
    b.引入一个中转数据结构,将其作为参数添加到提炼出的新函数的参数列表中
    c.检查第二阶段的参数,如果被第一阶段用到,将其移入中转数据结构中
    d.对第一阶段的代码运用提炼函数,让提炼出的函数返回中转数据结构

    4. 封装

    4.1. 封装记录

    目的: 封装可变数据,可以隐藏存储的细节和计算的过程
    实现:
    a.对持有记录的变量使用封装变量,将其封装到一个函数中
    b.创建类,记录封装起来,将记录变量的值替换为类的实例,在类上定义访问接口,用于返回原始记录。修改封装变量的函数,令其使用这个访问函数
    c.新建函数,让它返回该类的对象,而非原始的记录
    d.使用新的访问函数,替换原来返回记录的函数。
    e.删除类的原始记录的访问函数

    4.2. 封装集合

    目的: 封装可变数据,可以清楚的看到数据被修改的地方和修改方式,后续可以方便的修改数据结构。
    实现:
    a.如果集合的引用尚未被封装起来,先用封装变量封装它
    b.在类上添加用于添加和删除集合元素的函数
    c.查找所有引用点,如果调用方直接修改集合,令该处调用使用新的添加/移除元素函数。
    d.修改集合中的取值函数,使其返回一个副本

    4.3. 以对象取代基本类型

    目的: 当基础类型变量的使用不再简单时封装为对象
    实现:
    a.如果变量尚未被封装起来,先使用封装变量封装它
    b.创建一个类,类的构造函数保持这个数据值,并提供一个取值函数
    c.修改设值函数,令其创建一个新类的对象并将其存入字段,如果有必要的话,同时修改字段的类型声明
    d.修改取值函数,使其调用新类的取值函数,并返回结果
    e.修改明确清楚的函数名字,将引用对象改为值对象或将值对象改为引用对象,用以明确新对象时值对象函数引用对象

    4.4. 以查询取代临时变量

    目的: 可以避免在多个函数中编写相同的逻辑
    实现:
    a.检查变量在使用前是否已经计算完毕,检查计算它的代码是否能得到相同的值
    b.可将变量修改为只读
    c.将变量赋值代码提炼为函数
    d.应用内联变量手法移除临时变量

    4.5. 提炼类

    目的:
    a.一个类应该是一个清晰的抽象的,只处理一些明确的责任。
    b.当一个类不断新增功能,修改等变得复杂时,就要考虑将其功能进行分离。
    实现:
    a.决定如何分解类所负的责任
    b.创建新类,用以表现从旧类中分离出来的责任。
    c.构造旧类时,创建一个新类,建立旧类到新类的访问接口
    d.将分离功能相关的字段和函数搬移到新类
    e.去除不需要的接口

    4.6. 内联类

    目的: 如果一个类不再承担足够责任,不再有单独存在的理由
    实现:
    a.待内联类的public接口在目标类中创建对应函数
    b.修改源类public接口的所有引用点,使其调用目标类的接口
    c.将待内联类中的函数和数据搬移到目标类
    d.删除待内联类

    4.7. 隐藏委托关系

    目的: 不要使用委托类去获取被委托类的实例,然后调用其接口;如果被委托类的接口改变,那么所有客户端都需要修改
    实现:
    a.对于每个委托关系中的函数,在委托类端创建一个简单的委托函数
    b.调整客户端使其调用委托类的函数
    c.如果将来不再有客户端使用委托类,便可以移除委托对象中的函数

    4.8. 移除中间人

    目的: 如果委托类的特性变多,那么相应的转发函数就会变多,此时可以考虑移除转发函数
    实现:
    a.为委托对象创建一个取值函数
    b.对于每个委托函数,让其客户端转为连续的访问函数调用

    4.9. 替换算法

    目的: 用比较清晰的方式取代复杂的方式
    实现:
    a.整理待替换算法,保证它被提取到一个独立的函数中
    b.进行独立函数测试
    c.编写新算法
    d.测试新旧算法,如果运行结果相同,则可替换新算法

    5. 搬移特性

    5.1. 搬移函数

    目的:
    a.提高代码的模块化
    b.将关联紧密的上下文整合到一起
    实现:
    a.检查函数在当前上下文里引用的所有程序元素,考虑是否将它们一并搬移
    b.检查待搬移函数是否具备多态性
    c.将函数赋值一份到目标上下文中
    d.从源上下文中正确引用目标函数
    e.修改源函数使其成为一个委托函数
    f.对源函数使用内联函数,去除委托

    5.2. 搬移字段

    目的:
    a.如果修改一个数据结构时,总是需要同时修改另一条数据结构,那么说明字段放错了位置
    b.如果更新一个字段,需要同时在多个结构中做修改,也是一个需要搬移的征兆
    实现:
    a.确保源字段已经得到了良好的封装
    b.在目标对象上创建一个字段
    c.确保源对象里能正常引用目标对象
    d.调整源对象的访问函数,令其使用各目标对象的字段
    e.移除源对象上的字段

    5.3. 搬移语句到函数

    目的: 如果调用某个函数时,总有一些相同的代码也需要每次执行,那么需要考虑将代码合并到函数里
    实现:
    a.将重复代码段移动到紧邻目标函数位置
    b.如果目标函数只有一个调用点,那么将重复代码段移入目标函数中
    c.不止一个调用点,运用提炼函数,将目标函数共同提炼成新函数
    d.所有调用点替换为新函数
    e.运用内联函数,将目标函数内联到新函数中

    5.4. 搬移语句到调用者

    目的: 函数边界发生偏移,即以往多个地方共用的行为,现在会出现不同的行为时,需要将不同的部分移除出函数,搬移到调用者
    实现:
    a.如果待搬移的语句仅有一处调用,那么直接搬移到调用函数中
    b.如果有待搬移的语句有多处调用,那么需要先提炼函数,再搬移到调用函数中

    5.5. 以函数调用取代内联代码

    目的:
    a.消除重复代码
    b.如果内联代码时对已有函数的重复,此时使用已有函数替换内联代码
    实现: 将内联代码替换为对一个既有函数的调用

    5.6. 移动语句

    目的: 让存在管理的东西一起出现,可以使代码更容易理解
    实现:
    a.确定待移动的代码片段应该搬往何处,检查待搬移代码段与目标之间是否会有影响
    b.剪切待搬移代码段到目标位置

    5.7. 拆分循环

    目的: 为了减小多次循环次数,一个循环内做多件事情;那么修改循环时,就需要去理解两件事情
    实现:
    a.复制一遍循环代码
    b.识别并删除循环中的重复代码,使每个循环只做一件事情

    5.8. 移除死代码

    目的:
    a.阅读代码、理解软件运作原来时,无用代码确实会带来额外的思想负担。
    b.没有用到的代码就删除掉,就算以后有可能使用到,在真正使用到的时候可通过版本控制来重新添加
    实现:
    a.查看死代码时候还有地方在调用
    b.删除死代码

    6. 重新组织数据

    6.1. 拆分变量

    目的: 临时变量被多次赋值时就需要拆分
    实现:
    a.在待分解变量的声明及第一次被赋值处,修改其名称
    b.如果可以将新的变量声明为不可修改
    c.以该变量第二次被赋值处为界,使其使用新变量
    d.重复以上步骤,修改其他地方的赋值

    6.2. 字段改名

    目的: 数据结构中的字段,对于阅读者理解特别重要
    实现:
    a.如果作用域很小,则直接替换后测试
    b.如果记录还没有封装,则先封装记录
    c.在对象内部对私有字段改名,并调整内部访问该字段的函数
    d.如果构造函数使用了旧的字段名,则使用修改函数名字将其改名
    e.用于函数改名给访问函数改名

    6.3. 以查询取代派生变量

    目的:
    a.可变数据是软件中最大的错误源头之一
    b.在一处修改数据,可能在另一处造成难以发现的破坏
    c.去除可变数据不现实,但尽量把可变数据的作用域限制在最小范围
    实现:
    a.找出所有对变量做更新的地方。如果有必要用拆分变量分割各个更新点
    b.新建函数用于计算该变量的值
    c.用引入断言,断言该变量和计算函数始终给出同样的值
    d.修改读取该变量的代码,令其调用新建的函数
    e.用移除死代码去掉变量的声明和赋值

    6.4. 将引用对象改为值对象

    目的:
    a.如果为引用对象那么想要更新属性,则需要保留原对象不动,只更新属性
    b.如果为值对象那么想要更新属性,可以直接替换整个内部对象
    实现:
    a.检查重构目标是否为不可变对象,或者是否可修改为不可变对象
    b.用移除设值函数逐一去掉所有设值函数
    c.提供过一个基于值的相等性判断函数,在其中使用值对象的字段

    6.5. 将值对象改为引用对象

    目的:
    a.如果一个数据中可能包含多个记录,而这些记录都管理在同一个逻辑数据结构中时,即可使用引用对象,去除重复的逻辑数据结构。
    b.去除重复的数据结构,可以降低内存的使用情况
    c.对于修改来说,数据结构的副本在修改时需要找到所有的副本进行修改
    实现:
    a.为相关的对象创建一个仓库
    b.确保构造函数有办法找到关联对象的正确实例
    c.修改宿主对象的构造函数,令其从仓库中获取关联对象

    7. 简化条件逻辑

    7.1. 分解条件表达式

    目的: 降低条件表达式的复杂度,减少当条件较多时函数的规模,提升代码可读性。
    实现: 对条件判断和每个条件分支分别运用提炼函数手法

    7.2. 合并条件表达式

    目的:
    a.检查条件不相同但最终行为却一致
    b.如果认为检查项彼此独立,不能被视为同一次检查,那就不能合并
    实现:
    a.确定表达式没有副作用
    b.使用适当的逻辑运算,将表达式合并
    c.考虑对合并的表达式提炼为函数

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

    目的: 某个条件为真,则做一些处理后,直接退出函数
    实现:
    a.选择最外层需要被替换的条件逻辑,替换为卫语句
    b.如果多个卫语句引发同样的结果,则使用合并条件表达式合并
    场景:
    a.两个分支都属于正常行为
    b.一个分支为正常行为,一个分支为异常行为

    7.4. 以多态取代条件表达式

    目的:
    a.利用多态承载各个类型特有的行为,以去除重复的分支逻辑
    b.有一个基础逻辑,在其之上又有一些差异。可将基础逻辑放进基类,差异点放进子类
    实现:
    a.如果类不具备多态,使用工厂函数创建并返回实例对象
    b.调用方使用工程函数获取实例
    c.带有条件逻辑的函数移动到基类中
    d.重写基类中的条件逻辑函数,实现有差异的条件逻辑

    7.5. 引入特例

    目的: 一个数据结构的使用者都在检查某个特殊值,并且当这个特殊值出现时所作的处理都相同。如果有此问题,就要想办法把这个处理逻辑收拢到一起
    实现:
    a.给重构目标检查特例的属性,使其返回false
    b.创建一个特例对象,其中只有检查特性的属性,返回false
    c.对特例值比较的代码,提炼函数;使用到的地方使用新函数替换
    d.将新的特例对象引入代码中,可以从函数调用中返回,也可以在变换函数中生成
    e.修改特例函数的主题,函数中使用检查特例的属性
    f.使用函数组合成类活函数组合成变换,将特例搬移到特例对象中
    g.特例函数内联到使用到的地方

    7.6. 引入断言

    目的: 只用当某个条件为真时,该段代码才有效
    实现: 如果发现代码某个添加始终为真,就加入一个断言

    8. 重构API

    8.1. 查询函数和修改函数分离

    目的: 当查询和修改分离后,在任何地方都可以直接调用,不用操心其他的事情
    实现:
    a.复制整个函数,并以查询命名
    b.从新函数中删除与查询不相关的语句
    c.所有调用的地方进行替换
    d.原函数中去掉返回值

    8.2. 函数参数化

    目的: 两个或多个函数相似,自由字面值不同,可使用传入不同的参数,将其合并去除重复代码
    实现:
    a.从相似的函数中选择一个
    b.把需要作为参数的,添加到参数列表中
    c.修改所有调用处
    d.修改函数体,使其使用新传入的参数
    e.对其所有相似函数进行修改

    8.3. 移除标记参数

    目的:
    a.标记参数即函数内根据标记参数做不同的处理逻辑
    b.传入的参数影响了函数内部的控制流,这才是标记参数
    c.会隐藏掉函数调用中存在的差异
    实现:
    a.针对参数的每一种可能值,新建一个明确函数
    b.使用标记参数的地方,改用新函数

    8.4. 保持对象完整

    目的:
    a.可以更好的应对变化,如果将来需要增加字段,则可以方便扩展
    b.可以缩短函数参数列表
    实现:
    a.新建空函数,给以期望的参数列表
    b.新函数中调用旧函数,并把新参数映射到就函数参数列表中
    c.替换所有旧函数的调用为新函数
    d.将旧函数内联到新函数中
    场景: 从一个函数获取几个值,然后有将这几个值传入另一个函数

    8.5. 以查询取代参数

    目的:
    a.避免函数参数重复
    b.缩短参数列表
    实现:
    a.如果有必要,将参数的计算过程到提炼到独立的函数中
    b.函数体内引用的地方,修改为新提炼的函数
    c.去掉参数
    场景: 函数调用时传入一个值,但这个值函数自己可以容易获得

    8.6. 以参数取代查询

    目的:
    a.改变代码的依赖关系
    b.减少全局变量的引用
    c.不要将所有依赖关系都变成参数,会导致参数列表冗长重复
    实现:
    a.对查询操作的代码提炼变量,将其从函数体中分离出来
    b.对代码提炼函数
    c.使用内联变量,消除刚才提炼出来的变量
    d.对原来的函数使用内联函数
    e.新函数改名为原函数

    8.7. 移除设值函数

    目的:
    a.创建对象后,属性就不会改变的属性,去掉设值函数
    b.只在构造函数中调用一次的设值函数
    实现:
    a.构造函数无法确定具体值时,将参数作为构造函数参数;在构造函数中调用设值函数,对字段设值
    b.移除所有除了构造函数之外的设值函数调用,改为新的构造函数,并测试
    c.使用内联函数消去设值函数。如果可以把字段设置为不可变类型

    8.8. 以工厂函数取代构造函数

    目的: 解决无法根据环境或参数信心返回子类的实例或代理对象问题
    实现:
    a.新建工厂函数,使其调用构造函数
    b.将调用构造函数的代码修改为调用工厂函数
    c.缩小构造函数的可见范围

    8.9. 以命令取代函数

    目的:
    a.提供更大的灵活性和更强的表达能力
    b.命令对象会增加代码复杂性
    c.将复杂的函数拆解为多个方法,彼此之间通过成员变量进行数据共享
    实现:
    a.为需要封装的函数创建空类,并根据函数命名类
    b.使用搬移函数将函数移动到类中
    c.考虑给每个参数创建一个字段,在构造函数中添加对应的参数

    8.10. 以函数取代命令

    目的: 相对于命令对象,将处理简单的命令对象,变换为函数
    实现:
    a.运行提炼函数将命令对象的代码提炼到一个函数中
    b.对命令对象中使用的函数进行内联函数方式合并到新函数中
    c.将构造函数参数移动到新函数参数列表中
    d.移除死代码并删除命令类

    9. 处理继承关系

    9.1. 函数上移

    目的: 避免重复代码
    实现:
    a.检查待上移函数,确定它们完全一致
    b.检查函数体内的函数和属性能在基类中调用到
    c.基类中创建函数,并上移子类代码到基类新函数中
    d.逐个移除子类函数
    场景: 某个函数在各个子类中的函数体都相同

    9.2. 字段上移

    目的:
    a.减少重复的成员变量的声明
    b.有可能将使用改字段的行为从子类上移到基类中
    实现:
    a.针对待上移字段,检查它们的所有使用点,确认它们以同样的方式被使用
    b.如果这些字段的名称不同,先使用变量改名为它们取个相同的名字
    c.在基类中新建一个字段
    d.移除子类中的字段
    场景: 子类中出现重复的成员变量

    9.3. 构造函数本体上移

    目的: 消除构造函数中重复的语句
    实现:
    a.为基类创建构造函数
    b.将子类构造函数中公共语句移动到基类构造函数调用语句之后
    c.移除子类中的公用代码段。公共部分使用到的变量,作为基类的参数传给基类构造函数
    d.对于无法简单上移到基类的公共代码,先应用提炼函数,再运用函数上移提升

    9.4. 函数下移

    目的: 如果基类中的某个函数只与一个(或少数几个)子类有关,那么将函数下移到子类中。避免非通用函数出现在基类中。
    实现:
    a.将基类的函数本体复制到每一个需要此函数的子类中
    b.删除基类中的函数
    c.将函数移动到每一个需要使用它的子类中

    9.5. 字段下移

    目的: 如果某个属性只被一个或者一小部分子类用到,那么将其下移到子类中,避免非通用成员变量在基类中。
    实现:
    a.在所有需要该属性的子类中声明属性
    b.将其从基类移除
    c.将该属性从所有不需要它的子类中删除

    9.6. 以子类取代类型码

    目的: 可以使用多态的方式处理条件语句
    实现:
    a.自封装类型码字段
    b.任选一个类型码取值,创建子类,覆写类型码的取值函数,使其返回类型码的字面值
    c.创建一个选择器逻辑,把类型码参数映射到新的子类
    d.针对每个类型码取值,重复上述步骤
    e.使用函数下移和以多态取代条件表达式处理原本访问类型码的函数
    f.移除原有类型码的访问函数

    9.7. 移除子类

    目的: 子类功能变少,或者随着功能的改变被调整,不在需要时,去除子类
    实现:
    a.使用工厂函数取代构造函数,把子类的构造包装到基类的工厂函数中
    b.如有任何代码检查子类的类型,先用提炼函数把类型检查逻辑包装起来,然后将其搬移到基类
    c.新建一个属性表示子类类型
    d.将原本用来判断子类类型的属性使用新建的子类类型
    e.删除子类

    9.8. 提炼基类

    目的: 两个类在做相似的事情
    实现:
    a.新建类
    b.使用构造函数本体上移’函数上移和属性上移手法,提取子类的共同元素
    c.确保子类中的相同成分都上移到基类
    d.考虑将调用的地方使用基类的接口

    9.9. 折叠继承体系

    目的: 基类和子类差异很小时,将两者合并
    实现:
    a.使用属性上移、属性下移、函数上移和函数下移将所有元素都移动到一个类中
    b.即将删除的类的引用点修改为合并后的类
    c.移除将删除的类

    9.10. 以委托取代子类

    目的:
    a.降低继承之间的强依赖关系
    b.可以使接口更清晰、减少耦合
    实现:
    a.如果构造函数有多个调用者,首先用以工厂函数取代构造函数,把构造函数包装起来
    b.创建空的委托类,构造函数接受所有子类特有的数据项,并经常以参数的形式接受一个指向基类的引用
    c.基类中添加属性,用于安放委托对象
    d.修改子类的创建逻辑,使其初始化上述委托字段,放入委托对象示例
    e.选择一个子类的函数移入委托类
    f.使用搬移手法搬移上述函数
    g.如果被搬移的源函数还在子类之外被调用了,就把留在源类中的委托代码从子类移到超类,并在委托代码之前加上卫语句,检查委托对象存在。如果子类之外已经没有其他调用者,就用移除死代码去掉已经没人使、用的委托代码。
    h.重复上述步骤,直到子类中所有的函数都搬移到委托类

    9.11. 以委托取代基类

    目的:
    a.通过继承来复用现有的功能。
    b.基类的一些函数对子类不适用时,不应该使用继承获取基类的功能
    c.用到现有类的部分功能时使用委托
    实现:
    a.子类中新建一个属性,使其引用基类的一个对象,并将这个委托引用初始化为基类的新示例
    b.针对基类的每个函数,在子类中创建转发函数,将调用请求转发给委托引用
    c.当所有超类函数都被转发函数覆写后,就可以去掉继承关系

  • 相关阅读:
    leetcode74 搜索二维矩阵
    网桥、路由器和网关有什么区别?
    关于#matlab#的问题:地铁5G信号2.6G频段覆盖,人多时怎么设置微基站,基站部署位置如何,怎么根据地铁站的人流量确定小区怎么划分
    python+vue新生报到宿舍安排管理系统django flask
    大数据仓库flink1.13.2的ODS层存储代码示例
    CRM系统中的工作流管理及其重要性
    python数据分析之Pandas库(一)
    C语言-二叉树的最大深度
    Mybatis中的#{}和${}的区别
    iOS NSKeyedUnarchiver归档和读取
  • 原文地址:https://blog.csdn.net/qq_35699583/article/details/132752734