Fork me on GitHub

重构手法之简化函数调用【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……

posted @ 2017-12-02 10:18  NaYoung  阅读(729)  评论(0编辑  收藏  举报