Fork me on GitHub

重构手法之在对象之间搬移特性【1】

返回总目录

本小节目录

1Move Method(搬移函数)

概要

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被或者调用。

在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除。

动机

如果一个类有太多行为,或如果一个类与另一个类有太多合作而形成高度耦合,又或者使用另一个对象的次数比使用自己所驻对象的次数还多。那就要搬移函数。

搬移函数时,要根据“这个函数与哪个对象的交流比较多”来决定其移动路径。

范例

用一个表示“账户”的Account类来说明这项重构:

class Account
{
    private AccountType _accountType;
    private int _daysOverdrawn;
    /// <summary>
    /// 透支金额计费规则
    /// </summary>
    /// <returns></returns>
    double OverdraftCharge()
    {
        if (_accountType.IsPremium())
        {
            double result = 10;
            if (_daysOverdrawn > 7)
            {
                result += (_daysOverdrawn - 7) * 0.85;
            }
            return result;
        }
        return _daysOverdrawn * 1.75;
    }

    double BankCharge()
    {
        double result = 4.5;
        if (_daysOverdrawn > 0)
        {
            result += OverdraftCharge();
        }
        return result;
    }
}

AccountType类如下:

class AccountType
{
       public bool IsPremium()
       {
            return true;
       }
 }

假设有几种新账户,每一种都有自己的“透支金额计费规则”。所有我们将OverdraftCharge()搬移到AccountType类去。

首先要做的就是:观察OverdraftCharge()使用的每一项特性,考虑是否值得将它们与OverdraftCharge()一起移动。此例中,我们需要让_daysOverdrawn 字段留在Account类中,因为这个值不会随着不同种类的账户而变化。然后我们将OverdraftCharge()函数代码复制到AccountType中,并做相应调整。

class AccountType
{
    public double OverdraftCharge(int daysOverdrawn)
    {
        if (IsPremium())
        {
            double result = 10;
            if (daysOverdrawn > 7)
            {
                result += (daysOverdrawn - 7) * 0.85;
            }
            return result;
        }
        return daysOverdrawn * 1.75;
    }


    public bool IsPremium()
    {
        return true;
    }
}

然后将源函数的函数本体替换为一个简单的委托动作。

class Account
{/// <summary>
    /// 透支金额计费规则
    /// </summary>
    /// <returns></returns>
    double OverdraftCharge()
    {
        return _accountType.OverdraftCharge(_daysOverdrawn);
    }
}

重构到这里就可以结束了。当然了,我们也可以删除Account中的源函数。我们找到源函数的所有调用者,并将这些调用重新定向,改为调用Account的BankCharge()。

class Account
{
    private AccountType _accountType;
    private int _daysOverdrawn;
  
    double BankCharge()
    {
        double result = 4.5;
        if (_daysOverdrawn > 0)
        {
            result += _accountType.OverdraftCharge(_daysOverdrawn);
        }
        return result;
    }
}

此例中被搬移函数只引用了一个字段,所以只需将这个字段作为参数传给目标函数就行了。如果被搬移函数调用了Account中的另一个函数,可以将源对象传递给目标函数。

class AccountType
{
    public double OverdraftCharge(Account account)
    {
        if (IsPremium())
        {
            double result = 10;
            if (daysOverdrawn > 7)
            {
                result += (account.GetDaysOverdrawn() - 7) * 0.85;
            }
            return result;
        }
        return account.GetDaysOverdrawn()* 1.75;
    }


    public bool IsPremium()
    {
        return true;
    }
}

 小结

在搬移函数时,检查源类中被源函数所使用的一切特性,考虑它们是否也该被搬移。如果某个特性只被你打算搬移的那个函数用到,那就应该将它一并搬移。如果另有其他函数使用了这个特性,就可以考虑将使用该特性的所有函数全都一并搬移。

2Move Field(搬移字段)

概要

在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。

在目标类建立一个字段,修改源字段的所有用户,令它们改用新字段。

动机

在类之间移动状态和行为,是重构中必不可少的措施。随着系统的发展,我们会发现自己需要新的类,并需要将现有的工作责任拖到新的类中。

对于一个字段,在其所驻类之外的另一个类中有更多函数使用了它,就要考虑搬移这个字段。

范例

还是以Account类为例。

class Account
{
    private AccountType _accountType;

    private double _interestRate;

    double GetInterestForAmountByDays(double amount, int days)
    {
        return _interestRate * amount * days / 365;
    }
}

我们想要把_interestRate搬移到AccountType类中去。目前已经有数个函数引用了它,GetInterestForAmountByDays()就是其中之一。

我们在AccountType中建立一个_interestRate字段,并封装成属性。

class AccountType
{

    private double _interestRate;

    public double InterestRate
    {
        get => _interestRate;
        set => _interestRate = value;
    }
}

现在让Account类中访问的_interestRate字段的函数转而使用AccountType对象,并且删除Account类中的_interestRate字段。

class Account
{
    private AccountType _accountType;

    double GetInterestForAmountByDays(double amount, int days)
    {
        return _accountType.InterestRate * amount * days / 365;
    }
}

小结

对于C#来说,可能这个重构手法叫“搬移属性”更合适一点。因为基本上字段都是私有的,属性才是供其他函数访问的。搬移属性做法和范例中是一样的。

 

To Be Continued……

posted @ 2017-11-22 09:10  NaYoung  阅读(1569)  评论(0编辑  收藏  举报