Fork me on GitHub

重构手法之简化条件表达式【1】

返回总目录

本小节目录

1Decompose Conditional(分解条件表达式)

概要

你有一个复杂的条件(if-else if-else)语句。

从if、else if、else三个段落中分别提炼出独立函数。

动机

复杂的条件逻辑往往会导致程序复杂度上升。编写代码来检查不同的条件分支、根据不同的分支做不同的事往往又会导致函数过长。

将条件表达式分解为多个独立函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中对应的代码改为调用新建函数,从而更清楚地表达自己的意图。对于条件逻辑,将每个分支条件分解成新函数还可以突出条件逻辑,更清楚地表明每个分支的作用,并且突出每个分支的原因。

范例

假设要计算购买某样商品的总价,而这个商品在冬天和夏天的单价是不同的:

class Order
{
    public double Quantity { get; set; }

    public double WinterRate { get; set; }

    public double SummerRate { get; set; }

    public double WinterServiceCharge { get; set; }

    public double GetCharge(DateTime date)
    {
        double result;
        if (date.Month < 3 || date.Month > 6)
        {
            result = Quantity * WinterRate + WinterServiceCharge;
        }
        else
        {
            result = Quantity * SummerRate;
        }
        return result;
    }

}

现在把每个分支的判断条件都提炼到一个独立函数中:

class Order
{
    public double Quantity { get; set; }

    public double WinterRate { get; set; }

    public double SummerRate { get; set; }

    public double WinterServiceCharge { get; set; }


    public double GetCharge(DateTime date)
    {
        double result;
        if (NotSummer(date))
        {
            result = WinterCharge(Quantity);
        }
        else
        {
            result = SummerCharge(Quantity);
        }
        return result;
    }

    private bool NotSummer(DateTime date)
    {
        return date.Month < 3 || date.Month > 6;
    }
    private double SummerCharge(double quantity)
    {
        return quantity * SummerRate;
    }

    private double WinterCharge(double quantity)
    {
        return quantity * WinterRate + WinterServiceCharge;
    }
}

通过这段代码可看出整个重构带来的清晰性。

小结

像上面这样的情况,很多人都不会去提炼分支条件。因为这些分支条件往往非常短,看上去似乎没有提炼的必要。但是,尽管这些条件往往很短,在代码意图和代码自身之间往往存在不小的差距。就像上面那样,NotSummer(date)这个语句比原本的代码更好地表达自己的意图。原来的代码,我必须看看,想一想,才能说出其作用。当然了,这里看起来似乎很简单。即使是这样,提炼出来的函数可读性也更高一些。

2Consolidate Conditional Expression(合并条件表达式)

概要

你有一系列条件测试,都得到相同结果。

将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数。

动机

代码里经常有这样的检查:检查条件各不相同,最终行为却一致。如果发现这种情况,应该使用“逻辑与”和“逻辑或”将它们合并为一个条件表达式。

之所以合并条件代码,有两个原因。(1)合并后的条件代码会告诉你“实际上只有一次条件检查,只不过有多个并列条件需要检查而已”,从而使这一次检查的用意更清晰。(2)这项重构往往是为了使用Extract Method做好准备。

范例:使用逻辑或

class Amount
{
    public int Seniority { get; set; }

    public int MonthsDisabled { get; set; }

    public bool IsPartTime { get; set; }

    double DisablilityAmount()
    {
        if (Seniority < 2)
        {
            return 0;
        }
        if (MonthsDisabled > 12)
        {
            return 0;
        }
        if (IsPartTime)
        {
            return 0;
        }
        //compute the disability amount
        //your code here
        return 1;
    }
}

这段代码中,一连串的条件检查都在做同一件事情。对于这样的代码,上述检查等价于一个以逻辑或连接起来的语句:

class Amount
{
    public int Seniority { get; set; }

    public int MonthsDisabled { get; set; }

    public bool IsPartTime { get; set; }

    double DisablilityAmount()
    {
        if (Seniority < 2 || MonthsDisabled > 12 || IsPartTime)
        {
            return 0;
        }
        //compute the disability amount
        //your code here
        return 1;
    }
}

现在,我们观察这个新的条件表达式,并运用Extract Method将它提炼成一个独立函数,以函数名称表达该语句所检查的条件:

class Amount
{
    public int Seniority { get; set; }

    public int MonthsDisabled { get; set; }

    public bool IsPartTime { get; set; }

    double DisablilityAmount()
    {
        if (IsNotEligibleForDisability())
        {
            return 0;
        }
        //compute the disability amount
        //your code here
        return 1;
    }

    bool IsNotEligibleForDisability()
    {
        return Seniority < 2 || MonthsDisabled > 12 || IsPartTime;
    }
}

范例:使用逻辑与

class Rate
{
    public double GetRate()
    {
        if (OnVacation())
        {
            if (LengthOfService() > 10)
            {
                return 1;
            }
        }
        return 0.5;
    }
    private bool OnVacation()
    {
        return true;
    }
    private int LengthOfService()
    {
        return 9;
    }
}

这段代码可以变成这样:

class Rate
{
    public double GetRate()
    {
        if (OnVacation() && LengthOfService() > 10)
        {
            return 1;
        }
        return 0.5;
    }
    private bool OnVacation()
    {
        return true;
    }
    private int LengthOfService()
    {
        return 9;
    }
}

如果所观察的部分只是对条件进行检查并返回一个值,就可以使用三元操作符将这一部分变成一条return语句。因此,下列代码:

if (OnVacation() && LengthOfService() > 10)
{
    return 1;
}
return 0.5;

就变成了:

return (OnVacation() && LengthOfService() > 1) ? 1 : 0.5;

小结

那我们什么时候不需要合并表达式呢?

即我们认为这些检查的确彼此独立,的确不应该被视为同一次检查,那就不使用本项重构。

 

 

To Be Continued...

posted @ 2017-11-26 10:46  NaYoung  阅读(1337)  评论(0编辑  收藏  举报