zoukankan      html  css  js  c++  java
  • 重构手法之简化条件表达式【3】

    返回总目录

    本小节目录

    5Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)

    概要

    函数中的条件逻辑使人难以看清正常的执行路径。

    使用卫语句表现所有特殊情况。

    动机

    条件表达式通常有两种表现形式。(1)所有分支都属于正常行为;(2)条件表达式提供的答案中只有一种是正常行为,其他都是不常见的情况。

    如果两条分支都是正常行为,就应该使用形如if...else...的条件表达式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查被称为“卫语句”。

    该重构手法的精髓就是:给一条分支以特别的重视。

    范例

    下面的函数以特殊规则处理死亡员工、驻外员工、退休员工的薪资:

    class Amount
    {
        public bool IsDead { get; set; }
    
        public bool IsSeparated { get; set; }
    
        public bool IsRetired { get; set; }
    public double GetPayAmount() { double result; if (IsDead) { result = DeadAmount(); } else { if (IsSeparated) { result = SeparatedAmount(); } else { if (IsRetired) { result = RetiredAmount(); } else { result = NormalAmount(); } } } return result; } private double DeadAmount() { return default(double); } private double SeparatedAmount() { return default(double); } private double RetiredAmount() { return default(double); } private double NormalAmount() { return default(double); } }

    我们可以看到,这段代码中,非正常情况掩盖了正常情况的检查,所以应该用卫语句来取代这些检查,以提高程序清晰度。

    我们从最上面的条件动作开始:

    public double GetPayAmount()
    {
        double result;
        if (IsDead)
        {
            return DeadAmount();
        }
        if (IsSeparated)
        {
            result = SeparatedAmount();
        }
        else
        {
            if (IsRetired)
            {
                result = RetiredAmount();
            }
            else
            {
                result = NormalAmount();
            }
        }
        return result;
    }

    然后继续替换下一个:

    public double GetPayAmount()
    {
        double result;
        if (IsDead)
        {
            return DeadAmount();
        }
        if (IsSeparated)
        {
            return SeparatedAmount();
        }
        if (IsRetired)
        {
            result = RetiredAmount();
        }
        else
        {
            result = NormalAmount();
        }
    
        return result;
    }

    然后是最后一个:

    public double GetPayAmount()
    {
        double result;
        if (IsDead)
        {
            return DeadAmount();
        }
        if (IsSeparated)
        {
            return SeparatedAmount();
        }
        if (IsRetired)
        {
            return RetiredAmount();
        }
        result = NormalAmount();
        return result;
    }

    此时,result变量已经没有意义了,所以可以删掉,最终代码如下:

    class Amount
    {
        public bool IsDead { get; set; }
    
        public bool IsSeparated { get; set; 
    
        public bool IsRetired { get; set; }
        public double GetPayAmount()
        {
            if (IsDead)
            {
                return DeadAmount();
            }
            if (IsSeparated)
            {
                return SeparatedAmount();
            }
            if (IsRetired)
            {
                return RetiredAmount();
            }
            return NormalAmount();
        }
        private double DeadAmount()
        {
            return default(double);
        }
        private double SeparatedAmount()
        {
            return default(double);
        }
    
        private double RetiredAmount()
        {
            return default(double);
    
        }
        private double NormalAmount()
        {
            return default(double);
        }
    }

     范例:将条件反转

    class AdjustedCapital
    {
        public double Capital { get; set; }
    public double IntRate { get; set; } public double Income { get; set; } public double Duration { get; set; } public double GetAdjustedCapital() { double result = 0; if (Capital > 0) { if (IntRate > 0 && Duration > 0) { result = Income / Duration; } } return result; } }

    同样地,我们进行逐一替换。不过在插入卫语句时,需要将条件反转过来:

    public double GetAdjustedCapital()
    {
        double result = 0;
        if (Capital <= 0)
        {
            return result;
        }
        if (IntRate > 0 && Duration > 0)
        {
            result = Income / Duration;
        }
        return result;
    }

    再替换下一个:

    public double GetAdjustedCapital()
    {
        double result = 0;
        if (Capital <= 0)
        {
            return result;
        }
        if (IntRate <= 0 || Duration <= 0)
        {
            return result;
        }
        result = Income / Duration;
        return result;
    }

    最后,我们可以删除临时变量:

    public double GetAdjustedCapital()
    {
        if (Capital <= 0)
        {
            return 0;
        }
        if (IntRate <= 0 || Duration <= 0)
        {
            return 0;
        }
        return Income / Duration;
    }

     小结

    许多程序员都有这样一个观念:“每个函数只能有一个入口和一个出口。”现代编程语言都会限制函数只有一个入口。但“函数只有一个出口”,其实并不是那么管用。

    书中有这么一句话:嵌套的条件分支往往是由一些深信“每个函数只能有一个出口的”程序员写出的。但实际上,如果对函数的剩余部分不感兴趣,那就应该立即退出。 引导阅读者去看一些没有用的else片段,只会妨碍他们对程序的理解。

    6Replace Conditional with Polymorphism(以多态取代条件表达式)

    概要

    你手上有个条件表达式,它根据对象类型的不同而选择不用的行为。

    将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

    动机

    如果需要根据对象的不同类型而采取不同的行为,使用多态可以不用编写明显的条件表达式。

    有一组条件表达式,如果想添加一种新类型,就必须查找并更新所有的条件表达式。而使用多态,只需要建立一个新的子类,并提供适当的函数即可。这就大大降低了系统各部分之间的依赖,使系统升级更加容易。

    范例

    如下代码所示,OrderProcessor类的ProcessOrder方法根据Customer的类型分别执行一些操作:

    public abstract class Customer
    {
    }
    
    public class Employee : Customer
    {
    }
    
    public class NonEmployee : Customer
    {
    }
    public class Product
    {
        public int Price { get; set; }
    }
    public class OrderProcessor
    {
        public decimal ProcessOrder(Customer customer, IEnumerable<Product> products)
        {
            // do some processing of order
            decimal orderTotal = products.Sum(p => p.Price);
    
            Type customerType = customer.GetType();
            if (customerType == typeof(Employee))
            {
                orderTotal -= orderTotal * 0.15m;
            }
            else if (customerType == typeof(NonEmployee))
            {
                orderTotal -= orderTotal * 0.05m;
            }
            return orderTotal;
        }
    }

    重构后的代码如下,每个Customer子类都封装自己的算法,然后OrderProcessor类的ProcessOrder方法的逻辑也变得简单并且清晰了。

    public abstract class Customer
    {
        public abstract decimal DiscountPercentage { get; }
    }
    
    public class Employee : Customer
    {
        public override decimal DiscountPercentage => 0.15m;
    }
    
    public class NonEmployee : Customer
    {
        public override decimal DiscountPercentage => 0.05m;
    }
    public class Product
    {
        public int Price { get; set; }
    }
    public class OrderProcessor
    {
        public decimal ProcessOrder(Customer customer, IEnumerable<Product> products)
        {
            // do some processing of order
            decimal orderTotal = products.Sum(p => p.Price);
    
            orderTotal = orderTotal * customer.DiscountPercentage;
    
            return orderTotal;
        }
    }

    小结

    “以多态取代条件表达式”这个重构在很多时候会出现设计模式中(常见的工厂家族、策略模式等都可以看到它的影子),因为运用它可以省去很多的条件判断,同时也能简化代码、规范类和对象之间的职责。

    To Be Continued……

  • 相关阅读:
    C# 读写 ini 配置文件
    [转]VB 读写ini 配置文件
    js实现隔行变色-------Day40
    Camel Games借助AWS在爆发式增长中提供优质游戏体验
    深入浅出--UNIX多进程编程之fork()函数
    【玩转微信公众平台之八】 演示样例代码分析
    jQuery 选择具有特殊属性的元素
    下载超星或读秀图书时,怎么搞定完整书签?
    意外的php之学习笔记
    POJ 1182 (经典食物链 /并查集扩展)
  • 原文地址:https://www.cnblogs.com/liuyoung/p/7887107.html
Copyright © 2011-2022 走看看