『重构--改善既有代码的设计』读书笔记----Move Method

时间:2014-12-12 09:57:54   收藏:0   阅读:279

明确函数所在类的位置是很重要的。这样可以避免你的类与别的类有太多耦合。也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁。简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和别的类进行交互,调用后者或者被后者调用,那么你就要注意了,你要去判断这个类是否真正适合他原来所在的类。

简单来说,这套手法就是在该函数最常引用的新类中建立一个有着类似行为的新函数,让旧函数变成一个单纯的委托函数或者完全删掉。

Move Method是重构理论的支柱。如果一个类的责任太多,或者一个类和别的类有太多合作,耦合太高,都需要考虑Move Method让类变得简单点。你需要多浏览你自己写的类,观察类的职责是否太多,与别的类的耦合程度问题。当然,如果你进行了Move Field,你也应该做这样的检查,因为我们都知道,函数就跟字段有关,字段都被你移动了,函数肯定要再检查一遍(如果同时需要Move Field和Move Method,一般先进行Move Filed会比较简单)。一旦发现了这样的函数,你要去分析他的调用端和被调用端,如果存在多态,你也应该去考虑继承体系中他所重定义的函数,来判断最终的移动路径。

如果你不能肯定是否该移动一个函数,你就暂且别去考虑移动这个函数,你可以转而去分析其他函数,移动其他函数往往会比你做这个决定来的简单点。如果你发现你移动了其他函数之后你仍然无法判定是否要移动这个函数,那么显然此时的这个函数移动与不移动已经不重要了。

做法:

例子:

class Account...
double overdraftCharge()
{
    if (m_type.isPremium())
    {
        double result = 10;

        if (m_daysOverdrawn > 7)
        {
            result += (m_daysOverdrawn - 7) * 0.85;
            return result;
        }
        else
        {
            return m_daysOverdrawn * 1.75;
        }
    }
}

double bankCharge()
{
    double result = 4.5;
    if (m_daysOverdrawn > 0)
    {
        result += overdraftCharge();
        return result;
    }
}

private:
    AccountType m_type;
    int m_daysOverdrawn;

我们发现会根据type的不同进行相应的不同运算规则,这提示我们应该把函数放到变化的类中去,即应该放到AccoountType中去。首先,我们观察搬移函数,观察其中使用的源类特性,发现只有m_daysOverdrawn是源类的特性,但我们不选择将他搬移到目标类中而是放在源类中,因为我们可以断定,他不会随着账户的种类变化而变化,注意:这个技巧还是很重要的,将不变化的东西放到固定的类中,将容易变化的东西移动到相应的变化类中,可以让我们后期重构做更好的多态处理。对于这次重构很简单,我们可以直接以传参的方式传给目标函数。接下来,我们在AccountType中新建函数,并且命名,在这里我们可以选择同样的名称,然后把函数代码全部贴过去,然后做相应的改变

class AccountType...
double overdraftCharge(int daysOverdrawn)
{
    if (isPremium())
    {
        double result = 10;

        if (daysOverdrawn > 7)
        {
            result += (daysOverdrawn - 7) * 0.85;
            return result;
        }
        else
        {
            return daysOverdrawn * 1.75;
        }
    }
}

把属于自己的isPremium()变成直接调用,以参数的形式替代源类中自己的字段。当我们需要源类的特性的时候其实有四种途径

  1. 将这个特性也搬移到目标类中
  2. 建立或使用一个从目标类到源类的引用
  3. 将源对象当作参数传给目标函数
  4. 如果所需特性只是个变量,将它作为参数传给目标函数

在这次例子中我们用了第四种方式,只是简单的传递变量参数给目标函数。调整目标函数使之通过编译之后我们进行源函数的修改,让他变成一个简单的委托,因为我们在源类中已经存在的AccountType的字段,所以改起来很简单,改完之后进行编译测试。

class Account...
double overdraftCharge()
{
    return m_type.overdraftCharge(m_daysOverdrawn);
}

我们可以保留源函数现在这样的样子也可以删除,如果你要删除你就要多做一步,就是找到源函数的所有调用者,然后进行替换,比如Account中的bankCharge()函数用到了这个源函数就进行相应的替换,替换完成之后你就可以删除源函数的声明了。

double bankCharge()
{
    double result = 4.5;
    if (m_daysOverdrawn > 0)
    {
        result += m_type.overdraftCharge(m_daysOverdrawn);
        return result;
    }
}

如果被搬移的函数不是private,你就必须检查是否有其他类也使用了这个函数,因为C++是强类型语言,你可以简单的删除源函数的声明,让编译器来帮你进行查找。

因为在这个例子之中之用了源类的一个字段,所以我们可以简单的以传参的形式给目标函数,如果源函数中使用了源类的别的函数或者多个字段,我们就必须传源类对象给目标函数

class AccountType...
double overdraftCharge(Account *account)
{
    if (isPremium())
    {
        double result = 10;

        if (account->daysOverdrawn() > 7)
        {
            result += (account->daysOverdrawn() - 7) * 0.85;
            return result;
        }
        else
        {
            return account->daysOverdrawn() * 1.75;
        }
    }
}

比如我们要获取daysOverdrawn必须要通过源类的别的函数或者需要源类的多个特性,那么此时我们就必须传源类对象过去让目标函数中进行调用。如果目标函数中需要源类的太多特性,那么你就得进一步进行重构。通常,你需要Extract Method并将其中一部分移回源类。

原文:http://www.cnblogs.com/rickyk/p/4159062.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!