布尔值有太多缺点吗?枚举可能是一个不错的选择

图片来源:unsplash

使用布尔标志值来管理代码库中的状态机可能听起来是个好主意,但事实并非如此。布尔值可能是许多程序员接触的第一个数据类型。它非常简单,只有两种状态:true 和false。

随着代码的发展,它很容易产生代码复杂性、可读性和可扩展性方面的问题。

通常,标记参数会划分函数的逻辑,迫使函数根据值执行多个操作。这可能会导致业务逻辑混乱,并且代码库很容易得到以下树结构:

背景故事

下面的故事清楚地展示了状态机和函数参数中布尔参数的弱点。

一群软件开发人员曾经构建了一个模块来管理用户状态。其中一位开发人员坚持使用布尔值,因为该模块只需要两种状态:ONLINE 和OFFLINE,这看起来快速、简单、直接。尽管大多数人并不完全同意这个建议,但他们还是照办了。

最终,如下所示的函数开始填充代码库:

func setUserState(isUserOnline :Bool)

不久之后,团队里来了一位新成员,他想知道下面这句话的真正含义:

setUserState(true) //新人一直盯着这个。

虽然有人想出了一个看起来更好的函数名称(setUserOnline),但是一旦出现新的业务需求(包括另一个用户状态:BLOCKED),事情就变成了一场噩梦。让我们看看这些开发人员为解决这个问题做了什么。

三态布尔问题

布尔值通常表示两种状态,但在某些语言中(例如使用布尔对象的Java),可以使用null来分配第三种状态。在上下文中,BLOCKED 将设置为null。

布尔值有太多缺点吗?枚举可能是一个不错的选择

图片来源:unsplash

虽然这似乎不需要额外的布尔值来适应新的用户状态,但它很容易导致NullPointerExceptions。

此外,在不同情况下区分false 和null 可能很棘手。例如,当布尔属性game.isPlaying 为true 时,它清楚地表明游戏处于播放模式。但是当它为false 或null 时, false 表示游戏暂停或停止。

正如您所看到的, false 没有提供足够的信息来轻松识别和回忆其绑定状态,而三态布尔值只会使逻辑变得复杂。

此外,当系统需求包含另一个称为EXPIRED 的状态时会发生什么?由于现在有四个状态,这种方法无法解决。让我们看看大多数开发人员采用的另一种方法。

多个布尔值带来隐藏的依赖项

开发人员最终扩展了之前函数的签名,为新状态添加了两个布尔参数:

func setUserState(isUserOnline : Bool, isUserBlocked : Bool, isUserExpired : Bool)

看似满足业务需求的简单扩展被迫在代码库中引入隐藏的依赖项和大量新组合。

创建的两个隐藏依赖项是isUserOnline — isUserExpired 和isUserOnline — isUserBlocked。现在需要显式管理额外状态以避免状态冲突。例如,被阻止/过期的用户无法在线。以下是要处理的两种冲突状态的示例:

#条件1: isUserOnline:false 和isUserExpired: true#条件2: isUserOnline: false 和isUserBlocked: true

添加的状态越多,函数很容易变成一长串参数。事情变得不可持续,因为你最终会得到很多, || ,以及其他复杂的分支逻辑来处理互斥和相关布尔值。

布尔值具有类型安全性和可读性问题

使用多个布尔值,很可能将它们混合在一起,最终可能会传递错误的值(可能来自其他对象),并且编译器甚至不会做出反应。在重构和执行代码审查时,这可能是一场噩梦,需要编写大量单元测试来解决此类问题。

布尔值有太多缺点吗?枚举可能是一个不错的选择

图片来源:unsplash

此外,很容易忘记布尔变量的false 或true 值的真正含义,并且理解充满布尔值的函数调用(如下所示)只会变得非常困难:

设置用户状态(真,假,假)

有人可能会说,现在许多编程语言都支持命名参数,可以提高函数的可读性。但话又说回来,有可能意外地传递了反向或不正确的布尔值,但函数签名仍然匹配。

这个故事中的软件开发人员如果使用枚举而不是布尔值,就可以避免这些问题。

选用枚举,避免用布尔值

枚举数是一种数据类型,由一组可以以类型安全方式使用的命名值组成。虽然它可能看起来不像布尔值那么简单,但使用枚举或其他用户定义类型有助于避免设置具有多个分支的复杂if 语句。

枚举UserStates{case activecase inactivecaseBlockedcase 已过期}

1. 枚举清晰、简洁

枚举强制对所有状态进行命名,这使得理解它们的含义并创建自记录代码变得容易。同样,枚举清楚地表明这些值是相互排斥的,消除了对冲突状态的任何怀疑。

在函数中将枚举作为参数传递更干净,有助于避免神秘的布尔值。只需比较以下两行:

setUserState(true,false, false)//下面的版本更加简洁清晰。setUserState(UserStates.active)

2. 枚举是类型安全的

布尔值有太多缺点吗?枚举可能是一个不错的选择

对于枚举,您不能为其分配指定值以外的任何值,因为它是类型安全的,因此不可能意外交换值或传递无效状态,因为它可以被编译器发现。

并非所有语言都支持本机枚举,在这种情况下您可以创建自定义类型。例如,在JavaScript 中,可以通过“冻结”对象中的常量来解决这个问题:

constUserState={ ACTIVE: 1, INACTIVE: 2, BLOCKED: 3, EXPIRED: 4 }; Object.freeze(UserState);

3. 枚举使扩展和重构更容易

扩展枚举器中的值集更容易,因为与布尔值不同,可能的状态组合数量在每个新情况下不会加倍。

此外,许多编译器足够聪明,可以指示需要进行哪些更改才能适应新的枚举。例如,Swift 会抛出错误,而在其他语言中,很容易查明枚举中发生的所有情况。

用额外的新案例扩展已经存在的枚举是毫不费力的,因为数据类型保持不变,使得重构整个事情变得更容易。

图片来源:unsplash

当然,布尔值也不是没有用的。如果您确定状态是二元且互斥的,或者方法名称已经描述了状态(例如setEnabled(true)),则可以随意使用布尔值。

但通常情况下,需求会发生变化并且需要添加新的状态。因此值得尝试二元素枚举,它比布尔标志更安全。枚举有助于让您的代码面向未来,而无需跟踪布尔字段。

不要为了简单而滥用布尔值。枚举是一个不错的选择。

用户评论


志平

布尔值确实简单,但枚举能提供更多信息,也更易于维护。

    有13位网友表示赞同!


话扎心

我觉得枚举在很多场景下比布尔值更灵活,表达更清晰。

    有16位网友表示赞同!


面瘫脸

布尔值有时候太笼统了,枚举可以更具体地表达状态。

    有10位网友表示赞同!


柠栀

布尔值的问题在于它无法描述更多细节,枚举可以解决这个问题。

    有10位网友表示赞同!


若他只爱我。

对于一些需要多种状态的场景,枚举比布尔值更合适。

    有11位网友表示赞同!


西瓜贩子

感觉枚举更易于理解,代码也更清晰。

    有19位网友表示赞同!


不识爱人心

布尔值可能不够细致,枚举可以提供更多的选择。

    有14位网友表示赞同!


安陌醉生

有时候布尔值会造成理解上的歧义,枚举能避免这个问题。

    有20位网友表示赞同!


安之若素

用枚举代替布尔值,代码可读性提升了不少。

    有5位网友表示赞同!


凝残月

个人认为枚举更易于扩展,方便添加新的状态。

    有10位网友表示赞同!


淡抹丶悲伤

枚举可以提高代码的可维护性,毕竟代码越清晰越好。

    有15位网友表示赞同!


别伤我i

布尔值太简单了,枚举可以表达更多信息。

    有11位网友表示赞同!


致命伤

在一些需要明确状态的场景,枚举比布尔值更靠谱。

    有20位网友表示赞同!


你是梦遥不可及

感觉布尔值太死板了,枚举更灵活,更符合实际需求。

    有10位网友表示赞同!


看我发功喷飞你

枚举在处理复杂状态时,比布尔值更有优势。

    有8位网友表示赞同!


*巴黎铁塔

对于一些需要多状态选择的情况,枚举是不错的选择。

    有6位网友表示赞同!


开心的笨小孩

布尔值有时候太抽象了,枚举更具象,更容易理解。

    有9位网友表示赞同!


日久见人心

枚举可以避免布尔值带来的歧义,让代码更清晰。

    有10位网友表示赞同!


忘故

布尔值只适合简单的场景,枚举可以处理更复杂的情况。

    有7位网友表示赞同!


隔壁阿不都

感觉枚举更易于扩展,方便以后添加新的状态。

    有14位网友表示赞同!

上一篇
下一篇

为您推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

联系我们

0898-88881688

在线咨询: QQ交谈

邮箱: email@zhutibaba.com

工作时间:周一至周五,9:00-17:30,节假日休息