• 小酌重构系列[21]——避免双重否定


    避免双重否定

    在自然语言中,双重否定表示肯定。但是在程序中,双重否定会降低代码的可读性,使程序不易理解,容易产生错觉。
    人通常是用“正向思维”去理解一件事情的,使用双重否定的判断,需要开发者以“逆向思维”的方式去理解它的含义。
    另外,在写程序时,"!"符号很容易被疏忽和遗漏,一不小心则会编写出错误的代码,从而产生bug。
    所以,在程序中,我们应当尽量避免使用双重否定。

    优惠券是否未被使用?

    还是以在线商城给用户发放优惠券为例,由于优惠券的初始状态是未被使用的,所以设计人员将优惠券的使用状态设计为IsUnused。

    /// <summary>
    /// 优惠券
    /// </summary>
    public class Coupon
    {
        /// <summary>
        /// 是否未被使用
        /// </summary>
        public bool IsUnused { get; set; }
    }
    


    这样设计会带来两个小问题

    • IsUnused表示“优惠券是否未被使用”,这句话本身是比较拗口的,开发人员需要“逆向思维”去理解它的含义。
    • 在写程序时,如果要判断“优惠券已经被使用”,则需要编写比较绕弯的程序
    // 如果优惠券已经被使用了
    if (!coupon.IsUnused)
    {
        // 业务逻辑
    }
    

    这段代码如果没有第1行的注释,是比较难于理解的,也许你是用以下方式理解的。

    SNAGHTML38b01d61

    理解这段代码看起来颇为费劲,我们应该换种方式来理解它。

    image

    因此,将属性设计为IsUsed更为合适。

    /// <summary>
    /// 优惠券
    /// </summary>
    public class Coupon
    {
        /// <summary>
        /// 是否被使用
        /// </summary>
        public bool IsUsed { get; set; }
    }
    

    编写的判断语句,可读性良好,也易于理解。

    // 如果优惠券已经被使用了
    if (coupon.IsUsed)
    {
        // 业务逻辑
    }
    

    PS:设计程序毕竟不是唱Rap,你没必要把自己饶进去了,又把别人也绕进去,大家都能轻易读懂的代码才可能是好的代码。

    示例

    重构前

    这段代码使用!customer.IsNotFlagged判断“客户账户被标记”,如果没有注释,这个判断就比较难理解。

    public class Order
    {
        public void Checkout(IEnumerable<Product> products, Customer customer)
        {
            // 如果客户账户被标记了
            if (!customer.IsNotFlagged)
            {
                // 记录错误并返回
                return;
            }
    
            // 正常的订单处理流程
        }
    }
    
    public class Customer
    {
        public decimal Balance { get; private set; }
    
        public bool IsNotFlagged
        {
            get { return Balance < 30m; }
        }
    }
    

    程序本意是为了表达一个肯定的语义——“如果客户账户是被标记的”,既然如此,我们何不直接用肯定的语义来表示它呢?

    重构后

    重构后,代码读起来就更加直观了,也很容易被理解。

    public class Order
    {
        public void Checkout(IEnumerable<Product> products, Customer customer)
        {
            // 如果客户账户被标记了
            if (customer.IsFlagged)
            {
                // 记录错误并返回
                return;
            }
    
            // 正常的订单处理流程
        }
    }
    
    public class Customer
    {
        public decimal Balance { get; private set; }
    
        public bool IsFlagged
        {
            get { return Balance >= 30m; }
        }
    }
    

    小结

    在设计bool类型的属性时,不仅要表达清楚它所表示的业务含义,还应当考虑编写代码时的复杂性,尽量避免使用双重否定。

  • 相关阅读:
    Hsl PLC
    .NET平台常用框架整理
    SSH全注解实例详解
    word2vec (CBOW、分层softmax、负采样)
    pandas dataframe 一行变多行 (query pv统计term pv)
    python 按二维数组的某行或列排序 (numpy lexsort)
    基于决策树的分类算法
    【linux】 mail/mutt 发送邮件
    【python】含中文字符串截断
    【python】 判断纯ascii串
  • 原文地址:https://www.cnblogs.com/keepfool/p/5544091.html
Copyright © 2020-2023  润新知