• 重构手法之简化函数调用【4】


    返回总目录

    本小节目录

    8Replace Parameter with Methods(以函数取代参数)

    概要

    对象调用某个函数,并将所得结果做为参数,传递给另一个函数。而接受参数的函数本身也能够调用前一个函数。

    让参数接受者去除该项参数,并直接调用前一个函数

    动机

    如果函数通过其他途径获得参数值,那么它就不应该通过参数取得该值。过长的参数列会增加程序阅读者的理解难度,因此我们应该尽可能的缩短参数列的长度。

    一种缩减参数列的办法:看看“参数接受端”是否可以通过“与调用端相同的计算”来取得参数值。如果调用端通过其所属对象内部的另一个函数来计算参数,并在计算过程中“未曾引用调用端的其他参数”,那么就可以将这个计算过程转移到被调用端内,从而去除该项参数。

    范例

    以下代码用于计算定单折扣价格。

    class Price
    {
        public int Quantity { get; set; }
    
        public int ItemPrice { get; set; }
        public double GetPrice()
        {
            int basePrice = Quantity * ItemPrice;
            int discountLevel = Quantity > 100 ? 2 : 1;
            double finalPrice = GetDiscountedPrice(basePrice, discountLevel);
            return finalPrice;
        }
      
        private double GetDiscountedPrice(int basePrice, int discountLevel)
        {
            if (discountLevel == 2)
            {
                return basePrice * 0.1;
            }
            return basePrice * 0.05;
        }
    }

    首先,把计算折扣等级(discountLevel)的代码提炼成一个独立的函数:

    private int GetDiscountLevel()
    {
        return Quantity > 100 ? 2 : 1;
    }

    接下来替换所有的引用点,并使用Remove parameter去掉参数:

    class Price
    {
        public int Quantity { get; set; }
    
        public int ItemPrice { get; set; }
        public double GetPrice()
        {
            int basePrice = Quantity * ItemPrice;
            double finalPrice = GetDiscountedPrice(basePrice);
            return finalPrice;
        }
    
        private int GetDiscountLevel()
        {
            return Quantity > 100 ? 2 : 1;
        }
        private double GetDiscountedPrice(int basePrice)
        {
            if (GetDiscountLevel() == 2)
            {
                return basePrice * 0.1;
            }
            return basePrice * 0.05;
        }
    }

    使用相同方法,去除basePrice:

    class Price
    {
        public int Quantity { get; set; }
    
        public int ItemPrice { get; set; }
        public double GetPrice()
        {
            return GetDiscountedPrice();
        }
        private int GetBasePrice()
        {
            return Quantity * ItemPrice;
        }
        private int GetDiscountLevel()
        {
            return Quantity > 100 ? 2 : 1;
        }
        private double GetDiscountedPrice()
        {
            if (GetDiscountLevel() == 2)
            {
                return GetBasePrice() * 0.1;
            }
            return GetBasePrice() * 0.05;
        }
    }

    最后观察发现,可以对GetDiscountedPrice()函数使用Inline Method:

    class Price
    {
        public int Quantity { get; set; }
    
        public int ItemPrice { get; set; }
        public double GetPrice()
        {
            if (GetDiscountLevel() == 2)
            {
                return GetBasePrice() * 0.1
            }
            return GetBasePrice() * 0.05;
        }
        private int GetBasePrice()
        {
            return Quantity * ItemPrice;
        }
        private int GetDiscountLevel()
        {
            return Quantity > 100 ? 2 : 1;
        }
      
    }

     小结

    使用该重构手法可以缩减参数列,使程序更容易理解。

    9Introduce Parameter Object(引入参数对象)

    概要

    某些参数总是很自然地同时出现。以一个对象取代这些参数

    动机

    一组参数可能有几个函数同时使用,这些函数可能隶属于同一个类,也可能隶属于不同的类。这样的一组参数就是所谓的Data Clump(数据泥团),我们可以运用一个对象包装所有这些数据,再以对象取代它们。本项重构的价值在于缩短参数列。此外,新对象所定义的访问函数还可以使代码更具一致性,这又进一步降低了代码的理解和修改难度。

    范例

    /// <summary>
    /// 账项类
    /// </summary>
    class Entry
    {
        public DateTime ChargeDate { get; set; }
    
        public double Value { get; set; }
    
        public Entry(double value, DateTime chargeDate)
        {
            Value = value;
            ChargeDate = chargeDate;
        }
    }
    /// <summary>
    /// 账目类
    /// </summary>
    class Account
    {
        private List<Entry> _entries = new List<Entry>();
        /// <summary>
        /// 计算两个日期之间的账项总量
        /// </summary>
        /// <param name="start">开始日期</param>
        /// <param name="end">结束日期</param>
        /// <returns></returns>
        public double GetFlowBetween(DateTime start, DateTime end)
        {
            return _entries.Where(entry => entry.ChargeDate >= start && entry.ChargeDate <= end).Sum(entry => entry.Value);
        }
    }

    现在客户端要调用的话,可能代码如下:

    Account anAccount = new Account();
    anAccount.GetFlowBetween(DateTime.Now, DateTime.Now.AddMonths(1));

    我们现在以“范围对象”来取而代之。首先在Account类新建一个简单的数据容器,用以表示范围:

    class DateRange
    {
        public DateTime Start { get; }
    
        public DateTime End { get; }
    
        public DateRange(DateTime start, DateTime end)
        {
            Start = start;
            End = end;
        }
    }

    然后修改GetFlowBetween()函数的参数:

    /// <summary>
    /// 计算两个日期之间的账项总量
    /// </summary>
    /// <param name="range"></param>
    /// <returns></returns>
    public double GetFlowBetween(DateRange range)
    {
        return _entries.Where(entry => entry.ChargeDate >= range.Start && entry.ChargeDate <= range.End).Sum(entry => entry.Value);
    }

    现在客户端调用的代码可能变成这样:

    Account anAccount = new Account();
    anAccount.GetFlowBetween(new DateRange(DateTime.Now, DateTime.Now.AddMonths(1)));

    小结

    本项重构还可以带来更多的好处。当把这些参数组织到一起之后,往往很快可以发现可被移植新建类的行为。通常,将原本使用那些参数的函数对这一组参数会有一些共通的处理,如果将这些共同行为移动新对象中,可以减少重复代码。

    10Remove Setting Method(移除设值函数)

    概要

    类中的某个字段或者属性应该在对象创建时被设值,然后就不再改变。

    去掉该字段或者属性的所有设值函数。

    动机

    如果你为某个字段或者属性提供了设值函数,这就暗示了这个字段或者属性值可以被改变。如果你不希望在对象初创之后,此字段或者属性还有机会改变,那就不要为它提供设值函数。这样你的意图会更加清晰,并且可以排除其值被修改的可能性。

    范例

    看一个简单的例子:

    class Account
    {
        public string Id{ get; set; }
    
        public Account(string id)
        {
            this.Id ="ZZ"+id;
        }
    }

    以上代码可以修改为:

    class Account
    {
        public string Id { get; }
    
        public Account(string id)
        {
            this.Id = "ZZ"+id;
        }
    }

    To Be Continued……

  • 相关阅读:
    js获取数组,对象的真实长度
    http和https区别
    react调用setstate后发生了什么
    for in for of foreach及map的区别
    事件委托(事件代理)
    CSS隐藏元素的几种方法
    react一些扩展
    [软件构造]异常的捕获与自定义
    [软件构造]可能是笔记总结吧
    计算机系统大作业
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7922527.html
Copyright © 2020-2023  润新知