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


    返回总目录

    本小节目录

    6Replace Parameter with Explicit Methods(以明确函数取代参数)

    概要

    你有一个函数,其中完全取决于参数值而采取不同行为。针对该参数的每个可能值,建立一个独立的函数

    动机

    如果某个参数有多种可能的值,而函数内又以表达式检查这些参数值,并根据不同参数值做出不同的行为,就该使用本项重构;

    可以获得好处:“编译期代码检查”,“接口更清楚”(如果用参数值决定函数行为,那么函数用户不但需要观察该函数,而且还要判断参数是否“合法化”。——而合法的参数,很少在文档中提到,必须通过上下文,才能判断);

    考虑“编译期检验”的好处,为了获取一个清晰的接口,我们也值得这么做。

    范例

    下列代码根据不同的参数值,建立Employee之下不同的子类。

    class EmployeeType
    {
        static Employee Create(int type)
        {
            switch (type)
            {
                case 0:
                    return new Engineer();
                case 1:
                    return new Salesman();
                case 2:
                    return new Manager();
                default:
                    throw new ArgumentException("Incorrect type code value!");
            }
        }
    }
    
    class Employee
    {
    
    }
    
    class Engineer:Employee
    {
        
    }
    
    class Salesman:Employee
    {
       
    }
    
    class Manager:Employee
    {
        
    }

    由于这是一个工厂函数,不能实施Replace Conditional with Polymorphism,因为使用该函数时对象根本还没有创建出来。由于可以预见到Employee不会有太多新的子类,所以可以放心地为每个子类建立一个工厂函数,而不必担心工厂函数的数量会剧增。首先,根据参数值建立相应的新函数:

    static Employee CreateEngineer()
    {
        return new Engineer();
    }
    static Employee CreateSalesman()
    {
        return new Salesman();
    }
    static Employee CreateManager()
    {
        return new Manager();
    }

    找到函数的调用端,把诸如下面这样的代码:

    Employee kent = EmployeeType.Create(0);

    替换为:

    Employee kent = EmployeeType.CreateEngineer();

    替换完成之后,就可以可以删掉Create()函数了。

    小结

    如果函数根据参数做出不同的响应,那么就是需要重构的时候了。

    7Preserve Whole Object(保持对象完整)

    概要

    你从某个对象中取出若干值,将它们作为某一次函数调用中的参数,改为传递整个对象

    动机

    我们常常会将来自同一对象的若干数据项作为参数,传递给某个函数。这样做的问题在于:万一被调函数需要新的数据项,就必须查找并修改对此函数的所有调用。但是如果传递的是整个对象,则不会有此问题。

    该项手法的好处:(1)使参数列更加稳固;(2)提高代码的可读性。

    范例

    class Room
    {
        public bool WithinPlan(HeatingPlan plan)
        {
            int low = DaysTempRange().GetLow();
            int high = DaysTempRange().GetHigh();
            return plan.WithinRange(low, high);
        }
    
        public TempRange DaysTempRange()
        {
            return new TempRange();
        }
    }
    
    class HeatingPlan
    {
        private TempRange _range;
        public bool WithinRange(int low, int high)
        {
            return low >= _range.GetLow() && high <= _range.GetHigh();
        }
    }
    
    class TempRange
    {
        public int GetLow()
        {
            return 6;
        }
    
        public int GetHigh()
        {
            return 28;
        }
    }

    这里其实不必将TempRange对象的信息拆开来单独传递,只需将整个对象传递给WithinPlan()函数即可:

    class Room
    {
        public bool WithinPlan(HeatingPlan plan)
        {
            return plan.WithinRange(DaysTempRange());
        }
    
        public TempRange DaysTempRange()
        {
            return new TempRange();
        }
    }
    class HeatingPlan
    {
        private TempRange _range;
        public bool WithinRange(TempRange roomRange)
        {
            return roomRange.GetLow() >= _range.GetLow() && roomRange.GetHigh() <= _range.GetHigh();
        }
    }
    
    class TempRange
    {
        public int GetLow()
        {
            return 6;
        }
    
        public int GetHigh()
        {
            return 28;
        }
    }

    小结

    传递整个对象虽然好,但事情总是有两面性。如果传的是数值,被调用函数就只依赖于这些数值,而不依赖它们所属的对象。但是如果传递的是整个对象,被调用函数所在的对象就需要依赖参数对象。如果这会使依赖结构恶化,就不该使用本项重构。

    To Be Continued……

  • 相关阅读:
    footer在最低显示
    jQuery toast message 地址 使用
    linux 获取经过N层Nginx转发的访问来源真实IP
    Java Json格式的字符串转变对象
    多个机器获取微信access-token导致的有效性问题
    站点技术---301重定向
    C++技术问题总结-第8篇 STL内存池是怎么实现的
    IIS中遇到无法预览的问题(HTTP 错误 401.3
    梯度下降深入浅出
    COM-IE-(2)
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7900566.html
Copyright © 2020-2023  润新知