`
czjxdm
  • 浏览: 122225 次
社区版块
存档分类
最新评论

重构重构

 
阅读更多
如果你想重构,重要的前提就是有强力的测试.哪怕你有自动化重构工具在手.

如果你想对既有代码进行测试,你就必须先重构,因为代码根本就没有办法在测试工具中实例化.

……

新写的代码大多是可以先进行测试,然后再挂接到原有代码中.而对付遗留的代码,我们则需要一点点地把代码抠出来测试.修改遗留代码时,我们需要将代码解依赖出来,建立其测试,然后才对它进行修改.

并不是所有的重构手法都需要测试,特别是我们已经有了自动化的重构工具.下面说的一些解依赖技术就是一些特殊的重构技术.只要我们小步地前进,细心地操作,并借且自动化重构工具,无须测试就能进行重构.而这些重构技术的目标就是为重构出来的代码建立测试.一旦测试建立,则可对代码进行更为自信的修改,则可对代码进行更自信的进一步重构.

这些解依赖技术来自于<<Working effectively with legacy code1>> 第三部分解依赖技术.当然这里面的技术在<<Refactoring…>>中也有,只不过这里更强调的是解除依赖与测试.


我们虔诚追求的,乃是代码的可理解性。

(一)、Bad Smell
1、Duplicated  Code – 重复的代码
    这是最容易看到和被理解的bad smell,如果你在一个以上的地方看到相同的代码,它一定可以改善为复用,提炼出那一段相同代码。
如果重复代码是同一个class内的二个Method,那么直接提炼出来,然后在需要处调用;
如果重复代码位于二个互为兄弟的subClasses 中,需要把重复代码提到superclass内;
如果代码间只是类似,并不完全相同,要先分割它们的相同和异处,或者用Template Method 模式;
如果几个函数以不同的算法做相同的事,就选择较清晰的一个,并统一之;
如果二个不相关的classes 内出现相同的代码,应该考虑将代码提炼到一个独立的class中,然后在另一个class中调用。等等,方法比较多。要多读源码,参考在别人的代码中,这种情况都是怎么处理的。

2、Long Method  -- 过长的方法与函数
    在我不熟悉OO的时候,总觉得程序里到处充满了delegation- 委托,就是找不到它在哪里计算,那时候总以过程式的思维来阅读,处处都是疑惑。后来慢慢就习惯了小型的函数,并且书中讲它的优点与利益 --  解释能力、共享能力、选择能力。
让small method 容易理解的真正关键在于一个好名字。如果你能给method起个一目了然其作用的好名字,阅读者就可以通过名字了解其作用,一般不必去看内部怎样实现。我们应当遵循这样一个原则,每当感觉需要注释来说明一些什么的时候,就将要说明的东西写进独立函数,并以其用途(而不是实现手法)命名。
据说99%的情况下,只要用Extract Method ,找到函数适合集中在一起的地方,提炼出来形成新的Method,就可以将其改小。条件式和循环也常常是需要提炼的信号。

3、Large Class   -- 过大的类
    如果单一class做了太多了事情,这往往是代码“重复、混乱”的信号。
    可以试着将变量提出来、抽出subclass,抽出Interface,抽出domain object,逐步地把它分解变小。

4、Long Parameter List  -- 过长参数列
   参数太长会造成难以理解,参数太多还会带来前后不一致,不易使用,一旦你需要更多的数据,或对某一项改变,修改复杂度都会越来越高。一般情况下,这些参数都可以提为一个Object,即使它确实缺乏合事的归属对像,还可以创建一个参数对像,这样对参数的修改就化为对对像值域的修改。
   但是这里还要注意一点,如果写的方法参数过长,或可能的变化过于频繁,就需要考虑一下自己的结构设计是否合理。

5、Divergent Change -- 发散式变化
良好的设计期待在变化发生的时候,程序更容易修改。一旦需要,只在该改的地方来改,而不是若干地方都涉及。
针对某一外界变化的所有相应修改,都只应该发生在单一class中,这个class的所有内容都应该反映外界变化。为此,我们应当找出可能发生变化之处,然后将它们提炼至另一个class中。
着重指一个class受多种变化的影响。

6、Shotgun Surgery  -- 霰弹式修改
与“发散式变化”类似,它着重指,一个变化引发多个class相应修改。
应该把所有能预见到发生的变化放一个class.

7、Feature Envy   -- 依恋情结
这是一种经典的bad small ,某个函数为了计算一个问题,大量引用另一个对像中的数据。函数对某个class的兴趣高过对自己所在的host class 兴趣。

8、Data Clumps – 数据泥团
常常在很多地方看到相同的几笔数据:例如二个Classes 内的相同的值域、几个函数的相同的参数列表,这些总是在一起出现的数据,应该放在一个类里面。
可以这样来评测: 删掉这些数据中的一个,然后看其它的是否还有意义,如果其它的不再有意义,这就是个明确的信息,应该为它们单独产生一个类的。

9、Primitive Obsession – 基本型别偏执
    我们可以试着在小任务上运用小对像,例如,结合了数值和币别的money class,含起始值和结束值的range class,将原本单独存在的数值替换为对象。
    如果你有一组总是放在一起的值域、或在参数列表中看到基本型别的数据、或发现自己正在从Array中挑选数据。这些时候,不妨考虑下用Object 代替分散的数据。

10、Switch Statements
   从本质上来讲,switch语句的问题在于重复。一旦使用,你会发现同样的switch语句散步于不同的地点,如果要为它添加一个新的case,必须找到所有的switch语句并修改它们。这时,就要考虑用polymorphism多态。

11、Parallel Inheritance Hierarchies – 平行继承体系
    如果你为某个class增加subclass,时,必须也为另一个class相应增加一个subclass,如果你发现某个继承体系的class名称前缀和另一个继承体系的class名称前缀完全相同。消除这种重复性的一般策略是,让一个继承体系的实体引用另一个继承体系的实体,再接着用Move field,Move method。


      重构(Refactoring)技巧读书笔记 之一
General Refactoring Tips, Part 1

本文简要整理重构方法的读书笔记及个人在做Code Review过程中,对程序代码常用的一些重构策略。通过适当的重构代码,的确可以显著提高代码的质量,令人赏心悦目。毫无疑问,这些重构策略均来自于Martin Fowler的《重构-改善既有代码的设计》,只是如何在实际项目中灵活运用而已。(注:本文重构策略的名称及其大部分内容来自《重构-改善既有代码的设计》一书,Martin Fowler 著,侯捷等译)。
先看看重构的定义吧:
(1)Refactoring means rewriting existing source code with the intent of improving its design rather than changing its external behavior. The focus of refactoring is on the structure of the source code, changing the design to make the code easier to understand, maintain, and modify. - 来自Borland Together提供的文档,觉得这个定义很清晰明了。
(2)重构是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,已改进程序的内部结构。-来自Martin Fowler的定义。
不过,我一般使用一些重构的工具,如ReSharper for VS.Net v1.0和Borland Together for VS.Net v2.0,这些重构工具可以帮助你做很多事情,可以简化你许多工作,同时也可以避免出现一些错误。关于ReSharper for VS.Net v1.0的简单介绍,可以参考本人以前的一篇Posting【体验ReSharper V1.0 for VS.Net 2003 - Part I, 体验ReSharper V1.0 for VS.Net 2003 - Part II】。另外VS.Net 2005已经内置了重构功能。不过,目前这些重构工具还远远不能涵盖各种重构方法,有总比没有好了。
因此,掌握必要的重构技巧逐步成为对程序员基本的要求,重要的是在掌握这些技巧后,也有助于类库初期设计的质量,避免或减少代码的坏味道(bad smell)。

一、代码坏味道(Bad Smell in Codes)及其重构策略
1.尽量消除重复的代码,将它们合而为一
根据重复的代码出现在不同的地方,分别采取不同的重构的策略:
在同一个Class的不同地方:通过采用重构工具提供的Extract Method功能提炼出重复的代码, 然后在这些地方调用上述提炼出方法。
在不同Subclasses中:通过Extract Method提炼出重复的代码,然后通过Pull Up Method将该方法移动到上级的Super class内。
在没有关系的Classes中:通过对其中一个使用Extract Class将重复的代码提炼到一个新类中,然后在另一个Class中调用生成的新类,消除重复的代码。

2.拆解过长的函数
过长的函数在我们的日常代码中经常可见,在C#中常通过#region #endregion区隔为不同的功能区域。
重构策略:通过Extract Method将过长的函数按照功能的不同进行适当拆解为小的函数,并且给这些小函数一个好名字。通过名字来了解函数提供的功能,提高代码的理解性。

3.拆解过大的类
过大的类也经常见到,特别是类中含有大量的成员变量。
重构策略:通过Extract Class将一些相关成员变量移植到新的Class中,如Employee类,一般会包含有联系方式的相关属性(电话, Mobile,地址,Zip等等),则可以将这些移植到新的EmployeeContact类中。

4.过长的参数列
过长的参数列的主要问题是难以理解,并且难以维护。如果要增加新的参数或者删除某一参数,易造成参数前后不一致。
重构策略:如果可以通过向已存在的对象查询获取参数,则可通过Replace Parameter with Method,移除参数列,通过在函数内部向上述已存在的对象查询来获取参数。
如果参数列中若干参数是已存在对象的属性,则可通过Preserve Whole Object将这些参赛替换为一个完整对象,这样不仅提高代码的可读性,同时已易于代码今后的维护。
另外,还可以将若干不相关的参数,使用Introduce Parameter Object来创建一个新的参数类。不过,我个人觉得如果这些情况过多的话,会产生很多莫名其妙的参数类了,反而降低代码的可读性。

个人觉得前面4种坏味道比较显而易见,也比较容易处理。



  • e15.pdf (100.8 KB)
  • 下载次数: 7
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics