zoukankan      html  css  js  c++  java
  • 小酌重构系列[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类型的属性时,不仅要表达清楚它所表示的业务含义,还应当考虑编写代码时的复杂性,尽量避免使用双重否定。

  • 相关阅读:
    poj 2676 Suduku (dfs)
    poj 1562 Oil Deposits (dfs)
    poj 2907 Collecting Beepers (dfs)
    poj 1655 Balancing Act (树形dfs)
    poj 3411 Paid Roads (dfs)
    hdu 2896 病毒侵袭 (AC)
    hdu 3065 病毒侵袭持续中 (AC)
    poj 2251 Dungeon Master (bfs)
    java中debug使用
    Swing入门级小项目总结
  • 原文地址:https://www.cnblogs.com/keepfool/p/5544091.html
Copyright © 2011-2022 走看看