3. 重新组织函数

重构,无外乎就是重新组织变量(实例变量,静态变量,局部变量,临时变量),表达式,函数,类,继承机制(extends, implements)等,以提高代码的可读性,可维护性等。代码世界里不希望看到高富帅,一切都以“小”为美。单纯的结构只做一件事,只因一件事而变化,变化时尽量只修改少量的地方,复杂功能也仅仅是借助群体的力量,以组合或集成的方式来扩展。我们这里从函数开始,介绍一些可以达成这种目标的方法。

3.1 Extract Method(提炼函数)
当看见一个 过长的函数或者一段 需要注释才能让人理解用途的代码,就需要看下是否需要把这段代码放进一个 独立函数中。再次说明, 函数的长度并不是看有多少行,关键在于函数名称和函数本体之间的语义距离。如果一个函数可以比较方便的定义一个简短又清晰的名字来描述它的功能,那么这个函数的大小就是合适的。
Extract Method最大的困难就是处理 局部变量,而临时变量是其中一个主要的困难源头。

1. 局部变量最简单的情况是:被提炼代码段 只是读取这些变量的值,并不修改它们。可以简单地把临时变量 当做参数传给目标函数
2. 如果被提炼代码段对局部变量 赋值,问题就复杂了。被赋值的临时变量也分两种情况。
    (1)较简单的情况是:这个变量只在被提炼代码段中使用,就可以将这个临时变量的声明移到被提炼代码段中。
    (2)另一种情况是:被提炼代码段之外的代码也使用了这个变量。这就需要让目标函数 返回该变量改变后的值。
      如果需要返回的变量不止一个,又该怎么办?有几种选择,
      (1)提炼多个函数,每个函数返回一个临时变量。
      (2)提炼工作不好做,可以尝试运用replace temp with query(后面3.4会讲到)减少临时变量。如果即使这样做了,提炼依旧困难重重,就动用replace method with method object(后面3.8会讲到),这个重构手法不在乎代码中有多少临时变量。

3.2 Inline Method(内联函数)
这点与3.1做法相反,而是把函数代码直接写到使用它的地方。有时候我们会遇到某些函数,其内部代码和函数名称同样清晰易读,那么就可以去掉这个函数,直接在需要的地方使用代码。这通常是比较简单的函数,比如一行三元表达式。
另一种需要使用Inline Method的情况是,手上有一群组织部合理的函数。可以将他们内联到一个大型函数中,再从中提炼出组织合理的小型函数。Kent beck发现,实施replace method with method object之前先这么做,往往可以获得不错的效果。

3.3 Inline Temp(内联临时变量)
如果有一个临时变量,只被一个简单表达式赋值一次, 而它妨碍了其他重构手法。可以内联临时变量。比如:
double basePrice = anOrder.basePrice();
return (basePrice > 1000);
可以重构为:
return (anOrder.basePrice() > 1000);

也就是消除临时变量。
Inline temp多半是作为replace temp with query的一部分使用的。

3.4 Replace Temp with Query(以查询取代临时变量)
你的程序以一个临时变量保存某一表达式的运算结果。将这个表达式提炼到一个独立函数中,将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可以被其他函数使用。

Replace temp with query 往往是运用Extract method之前必不可少的一个步骤。局部变量会使代码难以被提炼,应该尽可能把他们替换为查询式。

3.5 Introduce Explaining Variable(引入解释性变量)
有一个复杂的表达式,将该复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称解释表达式的用途。
前面提到的都是在尽量消除临时变量,这一条确实引入临时变量。那么,应该在什么时候使用Introduct explaining variable呢?答案是:在Extract Method需要花费更大工作量时。如果要处理的是一个拥有大量局部变量的算法,那么使用Extract method绝非易事。这时候就会使用Introduct explaining variable来清理代码,然后再考虑下一步该怎么办。搞清代码逻辑之后,总是可以运用repalce temp with query把中间引入的解释性变量给去掉。况且,如果最终使用replace method with method object,那么中间引入的那些解释性临时变量也有其价值。

3.6 Split Temporary Variable (分解临时变量)
程序中有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。那么可以针对每次赋值,创造一个独立,明确的临时变量,而不是每次使用同一个名称的变量。


3.7 Remove Assignment to Parameters (移除对函数参数的赋值)
方法体内对传入参数进行了赋值操作,这时需要以一个临时变量 取代该参数的位置。这里说的赋值是指改变了参数的引用,而不是改变引用的对象的值,后者是完全正常合理的操作。


3.8 Replace method with method Object(以函数对象取代函数)
有一个大型函数,其中对局部变量的使用使你无法采用Extract Method,就可以将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段。然后可以在同一个对象中将这个大型函数分解为多个小型函数 。也就是新引入了一个class,根据待处理函数的用途为这个类命名,并且建立一个原函数所在的对象的final引用,其他局部变量也都变成这个class的一个域,这个class就只用来承载method的用途。

3.9 Substitute Algorithm(替换算法)
把某个算法替换为另一个更清晰的算法。

解决问题有好几种方法,但其中肯定有些方法比其他的更简单有效。“重构”可以把一些复杂东西 分解为较简单的小块,但有时候必须删掉整个算法,代之以较简单的算法。
使用这项重构之前,请先确定自己已经尽可能分解了原先函数。替换一个巨大而复杂的算法是困难的,只有先分解为教简单的小型函数,才能很有把握地进行算法替换工作。

上面我们发现有些方法是完全相反的,所以代码重构是没有固定法则的,很多时候需要我们分析,反复琢磨之后再做决定,怎样才能更容易理解,更容易后面维护。

猜你喜欢

转载自zoroeye.iteye.com/blog/2196863
今日推荐