避免双重否定

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

优惠券是否未被使用?

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

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

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

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

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

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

因此,将属性设计为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类型的属性时,不仅要表达清楚它所表示的业务含义,还应当考虑编写代码时的复杂性,尽量避免使用双重否定。

小酌重构系列目录汇总
关注keepfool

转载于:https://www.cnblogs.com/keepfool/p/5544091.html

小酌重构系列[21]——避免双重否定相关推荐

  1. 路漫漫其修远兮,吾将上下而求索——小酌重构系列[0]开篇有益

    相信博客园的读者大多都是千万"码农"中的一员,每个人都写过很多代码,但并不是每一个人都能写出高质量的代码. rome is not built in one day !--完成高质 ...

  2. 小酌重构系列[8]——提取接口

    前言 世间唯一"不变"的是"变化"本身,这句话同样适用于软件设计和开发. 在软件系统中,模块(类.方法)应该依赖于抽象,而不应该依赖于实现. 当需求发生&quo ...

  3. 小酌重构系列[19]——分解大括号

    概述 if else, for, while等是程序中最常用的语句,这些语句有一个共同点--它们的逻辑都封装在一对"{}"包围的代码块中.在实现复杂的业务逻辑时,会较多地用到这些语 ...

  4. 小酌重构系列[20]——用条件判断代替异常

    小酌重构系列[20]--用条件判断代替异常 参考文章: (1)小酌重构系列[20]--用条件判断代替异常 (2)https://www.cnblogs.com/keepfool/p/5513946.h ...

  5. 小酌重构系列[18]——重命名

    概述 代码是从命名开始的,我们给类.方法.变量和参数命名,我们也给解决方案.工程.目录命名.在编码时,除了应该遵守编程语言本身的命名规范外,我们应该提供好的命名.好的命名意味着良好的可读性,读你代码的 ...

  6. 小酌重构系列[4]——分解方法

    概述 "分解方法"的思想和前面讲到的"提取方法"."提取方法对象"基本一致. 它是将较大个体的方法不断的拆分,让每个"方法&quo ...

  7. 小酌重构系列[10]——分离职责

    概述 "分离职责"是经常使用的一个重构策略,当一个类担任的职责太多时,应按职责将它拆分成多个类,每个类分别承担"单一"的职责,也就是让每个类专心地做" ...

  8. 小酌重构系列[17]——提取工厂类

    概述 在程序中创建对象,并设置对象的属性,是我们长干的事儿.当创建对象需要大量的重复代码时,代码看起来就不那么优雅了.从类的职责角度出发,业务类既要实现一定的逻辑,还要负责对象的创建,业务类干的事儿也 ...

  9. 小酌重构系列[16]——引入契约式设计

    概述 试想这样一个场景,你提供了一些API给客户端调用,客户端传入了一些参数,然后根据这些参数执行了API逻辑,最终返回一个结果给客户端. 在这个场景中,有两个隐患,它们分别是: 客户端调用API时, ...

最新文章

  1. php 跨进程读写,php使用多个进程同时控制文件读写示例
  2. 头条一面:Spring IOC容器中只存放单例Bean吗?
  3. IOS中通知中心(NSNotificationCenter)的使用总结
  4. Java中final关键字的几种用法
  5. Java入门1.2.3—一个老鸟的Java学习心得
  6. Android 第十三课 SharedPreferences存储
  7. network怎么断点调试_pycharm 调试一些小技巧
  8. size ar objdump readelf binutils
  9. windows修改用户文件夹名称 更改用户名 修改C盘Users目录下文件夹名称
  10. php 邮箱附件 大小限制,Exmail+Postfix修改邮件附件大小
  11. springboot2.3.9使用spring data elasticsearch兼容低版本es(6.X)
  12. 世界主要的11种气候类型特点及分布
  13. python有趣小程序春节祝福-Python自动回复微信好新年祝福
  14. 大型C语言打怪小游戏——神魔
  15. 减轻压力保护脊椎,上学路上更轻松,Deuter多特护脊减负双肩背包体验
  16. linux上删除rime方案_安装linux中文输入法rime并配置
  17. SayAsYouWish:Fine-grained Control of Image Caption Generation with Abstract Scene Graphs ---- 论文阅读笔记
  18. 关于大数据,你应该知道的50个专业术语
  19. 系统性能优化策略 -- 持续优化更新
  20. WP系统一次订阅,终身锁屏同时显示农历和天气

热门文章

  1. 手机如何打开开发者模式
  2. c++《AVL树的概念》《AVL树的插入》《AVL树的旋转》《AVL树的验证》《AVL树的删除》《AVL树的性能》
  3. python输出word内容_使用python-docx生成Word文档
  4. 网络编程代码实例:多进程版
  5. iOS 常用第三方库及插件
  6. 人大金仓数据库KCA/KCP认证题库
  7. 修改微信电脑版的字体
  8. 11矩阵空间、秩1矩阵和小世界图
  9. 前后端分离-小项目-1前端布局
  10. Maven配置中央仓库