文档开发者中心
我的

【综合】Clean Code系列3:善用枚举替代常量,减少冗长泛滥的switch case语句

作者:HW-SQH


1. 在产品的Java代码里,经常有泛滥的常量使用

更糟糕的是,有一种大泥球像ModeContstant这样的XxxConstant类,里面包含了各种常量定义,例如HANDOFF,OTHER的。 还有一种是方法定义,其返回值数值或字串常量的。示例1:

这种方法返回特定数值或字串,其还包含错误码的情形,这叫带内错误指示器[1]。

常见的例子有:

- 一个有效的对象,或者为null
- 正整数表示有效返回值,-1表示有错误发生

这样的代码易使程序员忽略检查程序的错误码,或者使用错误的返回值进行后续的计算。可读性也不太好,程序员要特别小心地分析各种具体值的处置。 好在Java 1.5引入了枚举enum,Java 1.8引入了Optional,虽然晚了些。

在Java语言通用编程规范中,关于枚举有这样的描述:

由于枚举类是类,因此适用于格式化类的所有其他规则。 Java的枚举比较灵活强大,而且与switch/case结合较好,只要可能,优先使用。 枚举的使用场景:


- 布尔型的2元素值,例如isCelsius = true | false来表示摄氏|华氏,可用
- 变量值仅在一个固定范围内变化用 enum 类型来定义。例如 建议8.6.3的Keyboard例子


- 整数或字符串的枚举模式,蕴含有某种名称空间的,例如上面的Suit 例子,或者其它语言的ComparisonResult,避免-1、0、1的数字比较:

上面的示例1,可以优化为更加Clean的代码。如下所示,当不能构造一个枚举常量时返回empty Optional,就像JDK 12中的 Optional<Enum.EnumDesc<E>> describeConstable()。

Java编程规范中也有这一条:建议8.6.2 Java 8使用Optional代替null作为返回值或者可能的缺失值。

调用方然后可以通过Optional的ifPresent(action)和orElse(other)来抽取有效值处理。也可以在枚举类型中增加一个更加有韧性的工厂方法来查询枚举单例:

2. 虽然没有直接写if/else,但是流水账似地罗列了所有的条件动作

示例2,粘滞性(Viscosity):做正确的事情比做错误的事情更难(实现或变更一个需求时,生硬的方法很简单,保持系统设计的方法很难;或开发环境迟钝低效时,开发人员会倾向于做不会导致大规模重编译的改动,即使那些改动不再保持设计)。

进一步地,示例2,good:

上面valueOf(enumType, name)方法,可能抛出IllegalArgumentException。如果API提供者不能确信外部参数在合法范围,则要处理异常。

3. 小结

在《重构》第2版中,描述了24种代码坏味道。其中包括 基本类型偏执(Primitive Obsession)和重复的switch (Repeated Switches)语句。作者建议以子类取代类型码Type Code加上以多态取代条件语句的组合将它换掉。

这种枚举 + 接口的方式,在《Effective Java》中也有举例。实际上,它是更一般的函数式编程的代数数据类型,在Scala和Kotlin中是基于密封类+模式匹配,更加优雅。JDK 13有个预览特性,会把Switch语句增强为Switch表达式。

这篇介绍Rust语言的文章沅有芷兮:类型系统的数学之美,描述了Optional的好处:


表面上看它似乎和上面第一种方式没有区别,但一个形式化完备的类型让很多事情变成了可能。这个函数可以被 pipe,被 compose,调用者不必担心类型的泄露 —— 所有信息都已经在 type signature 里面了,编译器可以做更合适更严格的检查,也可以适当优化 —— 更重要的是,围绕着这个类型,我们可以把一堆原本不断出现在用户代码中的对结果判断的 if else / try catch 抽象出来,成为 Option 类型的一组 behavior,这样让用户代码变得清晰。同样的思路,在 Rust 里,exception 被抛弃,取而代之的是是 Result,也是一个 sum type。

[1] 《Java编码指南 - 编写安全可靠程序的75条建议》,Item 52:要避免使用带内错误指示器(in-band error indicator)

点赞
收藏
回复
12
分享
举报
浏览6847 发布于2019-11-21 03:17未知归属地
全部评论
最多点赞
最新发布
最早发布

值得多读

12楼回复于2019-11-21 08:48 未知归属地

这篇文章好得很

11楼回复于2019-11-21 08:48 未知归属地

支持起来

10楼回复于2019-11-21 08:10 未知归属地

阅,值得看三遍的好文

9楼回复于2019-11-21 08:10 未知归属地

系列文章真的很不错

8楼回复于2019-11-21 08:06 未知归属地

学习了,受益匪浅

7楼回复于2019-11-21 08:06 未知归属地

再给大神点个赞

6楼回复于2019-11-21 08:01 未知归属地

呦,这么好的帖子,顶起来

5楼回复于2019-11-21 08:00 未知归属地

好帖子必须顶起来

4楼回复于2019-11-21 06:22 未知归属地

例子列举的很好

3楼回复于2019-11-21 06:21 未知归属地
共2页
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

目录 收起
1. 在产品的Java代码里,经常有泛滥的常量使用
2. 虽然没有直接写if/else,但是流水账似地罗列了所有的条件动作
进一步地,示例2,good:
3. 小结
了解社区公约,与您携手共创和谐专业的开发者社区。