zoukankan      html  css  js  c++  java
  • 重构手法之在对象之间搬移特性【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……

  • 相关阅读:
    LDAP服务器的概念和原理简单介绍
    LDAP概念和原理介绍
    @ENABLEWEBSECURITY和@ENABLEWEBMVCSECURITY有什么区别?
    解决:javac: 无效的目标发行版: 1.8
    win10下,cmd,power shell设置默认编码为‘UTF-8’?
    windows 控制台cmd乱码(及永久修改编码)的解决办法
    学而不思则罔,思而不学则殆(读书要思考,灵活运用。考虑问题的时候,不要陷入空想,要去看书学一下才有用)(孔子亲测:吾尝终日不食,终夜不寝,以思,无益,不如学也),死记硬背不行,光自己琢磨不看书也不行
    【需求采集】用户访谈的注意点
    C++中回调(CallBack)的使用方法(其实就是类方法指针,我觉得你的方法易用性不好,虽然原理正确)
    arm cpu的架构及分类说明
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7858062.html
Copyright © 2011-2022 走看看