重构方法

重新组织函数

提炼函数 Extract Method

function printOwing($amount){
    printBanner();
    
    // print details
    echo NAME;
    echo $amount;
}

/** Refactor **/

function printOwing($amount){
    printBanner();
    printDetails($amount);
}

function printDetails($amount){
    echo NAME;
    echo $amount;
}

动机: 当遇到过长的函数或者一段需要注释才能让人理解的代码时使用。

当遇到局部变量时,要小心!

当发现源函数的参数被赋值时,应该使用Remove Assignments to Parameters方法。

内联函数-Inline Method

function getRating(){
    return moreThanFiveLateDeliveries() ? 2 : 1;
}

function moreThanFiveLateDeliveries(){
    return NUMBER_OF_LATE_DELIVERIES > 5;
}
/** R **/

int getRating(){
    return NUMBER_OF_LATE_DELIVERIES > 5 ? 2 : 1;
}

注意:重构函数不应具有多态性

内联临时变量-Inline Temp

$basePrice = AnOrder->basePrice();
return $basePrice > 1000;

/** R **/

return AnOrder->basePrice() > 1000;

动机:Inline Temp多半是作为Replace Temp with Query的一部分使用的。

查询渠道临时变量-Replace Temp with Query

$basePrice = $quantity * $itemPrice;
if($basePrice > 1000){
    return $basePrice * 0.95;
}else{
    return $basePrice * 0.98;
}

/** R **/

function basePrice(){
    return $quantity * $itemPrice;
}

if(basePrice() > 1000){
    return basePrice() * 0.95;
}else{
    return basePrice() * 0.98;
}

动机:将临时变量作为查询,同一个类中的所有函数均可以访问

注意:要确定下来final型的变量,为了更好的编写后续程序。

引入解释性变量-Introduce Explaining Variable

if((Platform->toUpperCase()->indexOf("MAC") > -1)
 && (Browser->toUpperCase()->indexOf("IE") > -1)
 && wasIntialized() && $resize > 0)
 {
     // Do sth
 }

/** R **/

final boolean isMacOs = Platform->toUpperCase()->indexOf("MAC") > -1;
final boolean isIEBrowser = Browser->toUpperCase()->indexOf("IE") > -1;
final boolean wasResized = resize > 0;

if(isMacOs && isIEBrowser && wasIntialized() && wasResized){
    // Do sth
}

动机:消除复杂表达式,用临时变量解释、归总每一步的意义

分解临时变量-Split Temporary Variable

拒绝临时变量的多次赋值,导致不同位置而具有不同意义。

移除对参数的赋值-Remove Assignments to Parameters

function discount($inputVal, $quantity, $yearToDate){
    if($inputVal > 50){
        $inputVal -= 2;
    }
}

/** R **/

function discount($inputVal, $quantity, $yearToDate){
    $result = $inputVal;
    if($inputVal > 50){
        $result -= 2;
    }
}

拒绝对参数赋值,保持对参数入出不变。

以函数对象取代类中函数-Replace Method with Method Object

如果一个函数过大(问题函数),考虑新建一个类来代替这个问题函数。

在对象之间搬移特性-决定责任归属

搬移函数-Move Method

如果一个类与另一个类有太多合作而形成高度耦合,则需要搬移函数,构建新类。

搬移字段-Move Field

同理上面的方法。

提炼类-Extract Class

如果某个类做了两个类做的事情,考虑新建一个类来处理。

将类内联化-Inline Class

如果某个类没有做太多事情,可以将这个类的特性搬移到另一个类中,移除原类。

隐藏委托关系-Hide Delegate

客户通过一个委托类来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。

移除中间人-Remove Middle Man

某个类做了过多的简单委托动作。这个时候可以考虑让客户直接调用受托类。

引入外加函数-Introduce Foreign Method

你需要位提供服务的类增加一个函数,但你无法修改这个类时。

可以在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。

$newStart = new Date(PreviousEnd->getYear(), PreviousEnd->getMonth(), PreviousEnd->getDate() + 1);

/** R **/

$newStart = nextDay(PreviousEnd);

function nextDay($date){
    return new Date($date->getYear(), $date->getMonth(), $date->getDate() + 1);
}

引入本地扩展-Introduce Local Extension

你需要为服务类提供一些额外函数,但你无法修改这个类。

那么就新建一个类,使它包含这些额外函数。让这个扩展类称为源类的子类或者包装类。

本地扩展的两种方式:

  1. 就是通过继承-构建子类来作出对源类的扩展。

  2. 通过构建包装类:将源类通过构造函数传入包装类,包装类对源类的每一个函数都进行再处理,通过包装类调用源类。

重新组织数据

通过对象来表示数据类型,即Replace Value with Object,即通过函数访问每一个数据。

把数组变成对象,可以更好的展露数组的数据结构。

自封装字段-Self Encapsulate Field

private $_low, $_high;

function includes($arg){
    return $arg >= self::$_low && $arg <= self::$_high;
}

/** R **/

private $_low, $_high;

function includes($arg){
    return $arg >= getLow() && $arg <= getHigh();
}

function getLow(){
    return self::$_low;
}

function getHigh(){
    return self::$_high;
}

以对象取代数据值-Replace Data Value with Object

动机:开发初期,往往决定以简单的数据表示简单的情况。但是随着开发深入,发现简单数据项也需要一些其它的概念或特殊行为。此时就可以把数据值抽象出来,变成一个可操作的对象。

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

当你从一个类衍生出许多彼此相等的实例时,希望将他们替换为同一个对象进行操作

那么就将这个值对象变成引用对象。这样来保证所有操作的实物是同一个实物(往往是真实世界中同一个物体)。

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

在分布式系统和并发系统中,不可变的值对象特别有用。因为你无需考虑他们的同步问题。

值对象本身应有一个特性:他们是不可变的。如果保证了这一点,就可以以多个对象表示同一个事物。如果是可变的,则考虑使用引用对象。

以对象取代数组-Replace Array with Object

复制‘被监视数据’-Duplicate Observed Data

view层数据和controller逻辑数据是需要拆分出来的。如果两层系统使用了同样的数据,那么view层可以通过观察者模式(Observer/Observable模式)来获得领域类的数据。

也可以使用事件监听器来取代观察者模型,每当领域对象发生变化,就向监听器发送一个事件。view层系统在适当时候调用update。

双向关联 & 单向关联

使用字面常量

封装字段

如果类中存在public字段,则将它声明为private,并提供相应的访问函数。

以类和子类取代类型码(Type Code)

参考

  1. 重构-改善既有代码的设计


niecprea
871 声望54 粉丝

Hello world!