膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 .
代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提;
.
作者 : 万境绝尘
转载请注明出处 :http://blog.csdn.net/shulianghan/article/details/20009689
.
1. 重复代码 (Duplicated Code)
用到的重构方法简介 : Extract Method(提炼函数), Pull Up Method(函数上移), From Template Method(塑造模板函数), Substitute Algorithm(替换算法), Extract Class(提炼类);
-- Extract Method(提炼函数) : 将重复的代码放到一个函数中, 并让函数名称清晰的解释函数的用途;
-- Pull Up Method(函数上移) : 将函数从子类移动到父类中;
-- From Template Method(塑造模板函数) : 不同子类中某些函数执行相似操作, 细节上不同, 可以将这些操作放入独立函数中, 这些函数名相同, 将函数上移父类中.
-- Substitute Algorithm(替换算法) : 将函数的本体替换成另外一个算法;
-- Extract Class(提炼类) : 建立一个新类, 将相关的函数 和 字段 从旧类搬移到新类;
重复代码坏处 : 重复的代码结构使程序变得冗长, 这个肯定要优化, 不解释;
同类函数重复代码: 同一个类中 两个函数 使用了相同的表达式;
-- 解决方案 : 使用 Extract Method(提炼函数) 方法提炼出重复的代码, 两个函数同时调用这个方法, 代替使用相同的表达式;
兄弟子类重复代码 : 一个父类有两个子类, 这两个子类中存在相同的表达式;
-- 代码相同解决方案 : 对两个子类 使用 Extract Method(提炼函数)方法, 然后将提炼出来的代码 使用 Pull Up Method(函数上移)方法, 将这段代码定义到父类中去;
-- 代码相似解决方案 : 使用 Extract Method(提炼函数)方法 将相似的部分 与 差异部分 分割开来, 将相似的部分单独放在一个函数中;
-- 进一步操作 : 进行完上面的操作之后, 可以运用 From Template Method(塑造模板函数) 获得一个 Template Method 设计模式, 使用模板函数将相似的部分设置到模板中, 不同的部分用于模板的参数等变量;
-- 算法切换 : 如果模板中函数的算法有差异, 可以选择比较清晰的一个, 使用Substitute Algorithm(替换算法) 将不清晰的算法替换掉;
不相干类出现重复代码 : 使用Extract Class(提炼类) 方法, 将重复的代码提炼到一个重复类中去, 然后在两个类中 使用这个提炼后的新类;
-- 提炼类存在方式 : 将提炼后的代码放到两个类中的一个, 另一个调用这个类, 如果放到第三个类, 两个类需要同时引用这个类;
2. 过长函数(Long Method)
用到的重构方法 : Extract Method(提炼函数),Replace Temp with Query(以查询取代临时变量),Introduce Parameter Object(引入参数对象),Preserve Whole Object(保持对象完整), Decompose Conditional(分解条件表达式);
--Extract Method(提炼函数) : 将代码放到一个新函数中, 函数名清晰的说明函数的作用;
--Replace Temp with Query(以查询取代临时变量) : 程序中将表达式结果放到临时变量中, 可以将这个表达式提炼到一个独立函数中, 调用这个新函数 去替换 这个临时变量表达式, 这个新函数就可以被其它函数调用;
--Introduce Parameter Object(引入参数对象) : 将参数封装到一个对象中, 以一个对象取代这些参数;
--Preserve Whole Object(保持对象完整) : 从某个对象中取出若干值, 将其作为某次函数调用时的参数, 由原来的传递参数 改为 传递整个对象, 类似于 Hibernate;
-- Replace Method with Method Object(以函数对象取代函数) : 大型函数中有很多 参数 和 临时变量, 将函数放到一个单独对象中, 局部变量 和 参数 就变成了对象内的字段, 然后可以在 同一个对象中 将这个 大型函数 分解为许多 小函数;
-- Decompose Conditional(分解条件表达式) : 将 if then else while 等语句的条件表达式提炼出来, 放到独立的函数中去;
小函数优点 : 小函数具有更强的 解释能力, 共享能力, 选择能力, 小函数维护性比较好, 拥有小函数的类活的比较长;
-- 长函数缺点 : 程序越长越难理解;
--函数开销 : 早期编程语言中子程序需要额外的开销, 所以都不愿意定义小函数. 现在面向对象语言中, 函数的开销基本没有;
-- 函数名称 : 小函数多, 看代码的时候经常转换上下文查看, 这里我们就需要为函数起一个容易懂的好名称, 一看函数名就能明白函数的作用, 不同在跳转过去理解函数的含义;
分解函数结果 : 尽可能分解函, 即使函数中只有一行代码, 哪怕函数调用比函数还要长, 只要函数名能解释代码用途就可以;
-- 分解时机 : 当我们需要添加注释的时候, 就应该将要注释的代码写入到一个独立的函数中, 并以代码的用途命名;
-- 关键 : 函数长度不是关键, 关键在于 函数 是 "做什么", 和 "如何做";
常用分解方法 : Extract Method(提炼函数) 适用于 99% 的过长函数情况, 只要将函数中冗长的部分提取出来, 放到另外一个函数中即可;
参数过多情况 : 如果函数内有大量的 参数 和 临时变量, 就会对函数提炼形成阻碍, 这时候使用 Extract Method(提炼函数) 方法就会将许多参数 和 临时变量当做参数传入到 提炼出来的函数中;
-- 消除临时变量 : 使用 Replace Temp with Query(以查询取代临时变量) 方法消除临时元素;
-- 消除过长参数 : 使用 Introduce Parameter Object(引入参数对象) 和 Preserve Whole Object(保持对象完整) 方法 可以将过长的参数列变得简洁一些;
-- 杀手锏 : 如果使用了上面 消除临时变量和过长参数的方法之后, 还存在很多 参数 和 临时变量, 此时就可以使用 Replace Method with Method Object(以函数对象取代函数方法) ;
提炼代码技巧 :
-- 寻找注释 : 注释能很好的指出 代码用途 和 实现手法 之间的语义距离, 代码前面有注释, 就说明这段代码可以替换成一个函数, 在注释的基础上为函数命名, 即使注释下面只有一行代码, 也要将其提炼到函数中;
-- 条件表达式 : 当 if else 语句, 或者 while 语句的条件表达式过长的时候, 可以使用Decompose Conditional(分解条件表达式) 方法, 处理条件表达式;
-- 循环代码提炼 : 当遇到循环的时候, 应该将循环的代码提炼到一个函数中去;
3. 过大的类 (Large Class)
用到的重构方法 : Extract Class(提炼类), Extract Subclass(提炼子类), Extract Interface(提炼接口), Duplicate Observed Data(复制被监视的数据);
-- Extract Class(提炼类) : 一个类中做了两个类做的事, 建立一个新类, 将相关的字段和函数从旧类中搬移到新类;
-- Extract Subclass(提炼子类) : 一个类中的某些特性只能被一部分实例使用到, 可以新建一个子类, 将只能由一部分实例用到的特性转移到子类中;
-- Extract Interface(提炼接口) : 多个客户端使用类中的同一组代码, 或者两个类的接口有相同的部分, 此时可以将相同的子集提炼到一个独立接口中;
-- Duplicate Observed Data(复制被监视的数据) : 一些领域数据放在GUI控件中, 领域函数需要访问这些数据; 将这些数据复制到一个领域对象中, 建立一个观察者模式, 用来同步领域对象 和 GUI对象的重要数据;
实例变量太多解决方案 : 使用 Extract Class (提炼类) 方法将一些变量提炼出来, 放入新类中;
-- 产生原因 :如果一个类的职能太多, 在单个类中做太多的事情, 这个类中会出现大量的实例变量;
-- 实例变量多的缺陷 : 往往Duplicate Code(重复代码)与Large Class(过大的类)是一起产生的;
-- 选择相关变量 : 选择类中相关的变量提炼到一个新类中, 一般前缀, 后缀相同的变量相关性较强, 可以将这些相关性较强的变量提炼到一个类中;
-- 子类提炼 : 如果一些变量适合作为子类, 使用Extract Subclass(提炼子类) 方法, 可以创建一个子类, 继承该类, 将提炼出来的相关变量放到子类中;
-- 多次提炼 : 一个类中定义了20个实例变量, 在同一个时刻, 只使用一部分实例变量, 比如在一个时刻只使用5个, 在另一时刻只使用4个 ... 我们可以将这些实例变量多次使用 提炼类 和 子类提炼方法;
代码太多解决方案 :
-- 代码多的缺陷 : 太多的代码是 代码重复, 混乱, 最终走向项目死亡的源头;
-- 简单解决方案 : 使用 Extract Method (提炼函数) 方法, 将重复代码提炼出来;
-- 提炼类代码技巧 : 使用 Extract Class(提炼类) 和 Extract Subclass(子类提炼) 方法对类的代码进行提炼, 先确定客户端如何使用这个类, 之后运用 Extract Interface(提炼接口) 为每种使用方式提炼出一个接口, 可以更清楚的分解这个类;
-- GUI类提炼技巧 : 使用 Duplicate Observed Data(复制被监视的数据) 方法, 将数据 和 行为 提炼到一个独立的对象中, 两边各保留一些重复数据, 用来保持同步;
4. 过长参数列 (Long Parameter List)
使用到的重构方法简介 : Replace Parameter with Method(以函数取代参数), Preserve Whole Object(保持对象完整), Introduce Parameter Object(引入参数对象);
-- Replace Parameter with Method(以函数取代参数) : 对象调用 函数1, 将结果作为 函数2 的参数, 函数2 内部就可以调用 函数1, 不用再传递参数了;
-- Preserve Whole Object(保持对象完整) : 将对象中的一些字段是函数的参数, 直接将对象作为函数的参数, 由传递多个参数改为传递封装好的对象;
-- Introduce Parameter Object(引入参数对象) : 将函数参数封装在一个对象中;
参数列过长 :
-- 函数数据来源 : ① 参数, 将函数中所需的数据都由参数传入; ② 将函数中所用到的数据设置在全局数据中, 尽量不要使用全局数据;
-- 对象参数 : 使用对象封装参数, 不需要把函数需要的所有数据用参数传入, 只需要将函数用到的数据封装到对象中即可;
-- 面向对象函数参数少 : 面向对象程序的函数, 函数所用的数据通常在类的全局变量中, 要比面向过程函数参数要少;
普通参数和对象参数对比 :
-- 参数过长缺陷 : 太多的参数会造成函数 调用之间的 前后不一致, 不易使用, 一旦需要更多数据, 就要修改函数参数结构;
-- 对象参数优点 : 使用对象传递函数, 如果需要更多的参数, 只需要在对象中添加字段即可;
参数的其它操作 :
-- 函数取代参数 : 在对象中 执行一个 函数1 就可以取代 函数2 的参数, 就要使用 Replace Parameter with Method(以函数取代参数) 方法;
-- 对象代替参数 : 函数中来自 同一个对象的 多个参数 可以封装在这个对象中, 可以将这个封装好的对象当做参数, 使用Preserve Whole Object(保持对象完整) 方法;
-- 创建参数对象 : 如果找不到合适的对象封装这些参数数据, 可以使用 Introduce Parameter Object(引入参数对象) 方法制造一个参数对象;
对象依赖与函数参数之间的平衡 : 二者是相对的, 下面一定要选择一种不利状况;
-- 避免依赖 : 函数参数传递对象, 那个函数所在的对象 与 这个参数对象依赖关系很紧密, 耦合性很高, 这时候就要避免依赖关系, 将数据从对象中拆出来作为参数;
-- 参数太长 : 如果参数太长, 或者变化太频繁, 就要考虑是否选择依赖;
5. 发散式变化 (Divergent Change)
对于这个在我所在的研发团队中这个问题很严重, 因为做的是远程医疗系统, 在Android上要支持许多医疗设备, 每次添加医疗设备都会死去活来;
使用到的重构方法简介 : Extract Class(提炼类);
期望效果 : 当我们添加新功能的时候, 只需要修改一个地方即可, 针对外界变化相应的修改, 只发生在单一类中, 如果做不到这一点, 就意味着程序有了坏味道 Divergent Change;
发散式变化 :
-- 出现效果 : 如果对程序进行例行维护的时候, 添加修改组件的时候, 要同时修改一个类中的多个方法, 那么这就是 Divergent Change;
-- 修改方法 : 找出造成发散变化的原因, 使用 Extract Class(提炼类) 将需要修改的方法集中到一个类中;
6. 霰弹式修改 (Shotgun Surgery)
使用到的重构方法简介 : Move Method(搬移函数), Move Field(搬移字段), Inline Class(内联化类);
-- Move Method(搬移函数) : 类A 中的 方法A 与 类B 交流频繁, 在类B中创建一个与 方法A 相似的 方法B, 从方法A 中 调用 方法B, 或者直接将方法A删除;
-- Move Field(搬移字段) : 类A 中的 字段A 经常被 类B 用到, 在类B 中新建一个字段B, 在类B 中尽量使用字段B;
-- Inline Class(内联化类) : 类A 没有太多功能, 将类A 的所有特性搬移到 类B中, 删除类A ;
霰弹式修改坏味道: 遇到的每种变化都需要在许多不同类内做出小修改, 即要修改的代码散布于四处, 不但很难找到, 而且容易忘记重要的修改, 这种情况就是霰弹式修改;
-- 注意霰弹式修改 与 发散式变化 区别 : 发散式变化是在一个类受多种变化影响, 每种变化修改的方法不同, 霰弹式修改是 一种变化引发修改多个类中的代码;
-- 目标 : 使外界变化 与 需要修改的类 趋于一一对应;
重构霰弹式修改 :
-- 代码集中到某个类中 : 使用 Move Method(搬移函数) 和 Move Field(搬移字段) 把所有需要修改的代码放进同一个类中;
-- 代码集中到新创建类中 : 没有合适类存放代码, 创建一个类, 使用 Inline Class(内联化类) 方法将一系列的行为放在同一个类中;
-- 造成分散式变化 : 上面的两种操作会造成 Divergent Change, 使用Extract Class 处理分散式变化;
.
作者:万境绝尘
转载请注明出处:http://blog.csdn.net/shulianghan/article/details/20009689
.
分享到:
相关推荐
1.重构要求 2.重构的工作 3.代码的bad smell 4. 重构的例子
这个项目是一个基于Java的检测器,可以检测Fowler等人的五个。 (1999) 的代码不良气味:数据块、开关语句、推测一般性、消息链和中间人,来自 Java 源代码。
顶级程序员培训公司Industrial Logic, Inc. 提供。重构技术的随身小纸条,详尽的代码坏味和对应的重构。强烈推荐给读过《重构:改善既有代码的设计》的人
自动代码气味检测器自动代码气味检测器是IntelliJ IDEA插件,致力于自动检测和纠正Java代码中的代码气味。下载及安装可以按照以下步骤直接在IntelliJ IDEA中下载该插件: 按Ctrl + Alt + S或选择文件| 设置(适用于...
1.版本:matlab2014/2019a/2021a ...3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
【智能优化算法-气味代理优化算法】基于气味代理优化算法求解单目标优化问题附matlab代码(SAO,Smell Agent Optimization)标准.zip
检测JavaScript类的内聚耦合Code Smell.pdf
沪教版一年级英语下册(牛津版) Unit 3 Taste and smell 第1课时 教案 教学设计 .pdf
代码味道重构练习 此存储库中的每个分支都有给定语言的练习。 原始源材料(在java分支中)是来自(C) Jason Gorman。 有关 TDD 和重构代码异味,请查看。
这是一个Visual Studio Code扩展,用于检测python代码中的不安全编码做法。 这些不安全的编码做法也称为安全气味。 这些气味可能为软件系统的利用留出空间,并导致安全漏洞。 为了帮助从业者,已经开发了此工具以在...
Coca 是一个用于系统重构、系统迁移和系统分析的工具箱。它可以分析代码中的 badsmell,行数统计,分析调用与依赖,进行 Git 分析,以及自动化重构等。
分享了气味剂算法源代码及其原文,更多算法可进入空间查看
fluent udf 多孔介质模型,用来解决多孔介质模拟的模型建立
论文《A Textual-based Technique for Smell Detection》pdf全
extractMethodP 一个从Java代码中提取Method的eclipse插件,创建一个新类作为method参数。解决Long Parameters Code Smell的方法。代码气味在计算机编程中,代码气味,也称为代码中的不良气味,是指程序源代码中任何...
code_smell_extractor_ci 代码气味提取器在github上的项目安装要做Git: : PMD: : 系统开发Linux运行程序您必须在linux上,并且必须修改src / main / java / utilities / Utility.java中的变量: PATH_PMD =“ [您...
程序员常刷题 ##1。 ...如果您需要它们,您的类可能太大了。 重构! ################################################ # Code banners are a code smell ######################################
findbugs是一个开源的eclipse 代码检查工具;它可以简单高效全面地帮助我们发现程序代码中存在的bug,bad smell,以及潜在隐患. 下载的文件包含如下: 1.findbugs的eclipse插件source包 2.安装及使用说明文档
很好的气味-它使您的代码闻起来很香! 用于python最佳实践和鲜为人知的技巧的整理/重构库 该工具尝试通过使用您可能不知道的语言功能和标准库功能,找到可能使更多的Python代码,更漂亮的代码位 例如直接嵌套的循环...
findbugs是一个开源的代码检查工具;它可以简单高效全面地帮助我们发现程序代码中存在的bug,bad smell,以及潜在隐患。