编程实战:如何管理代码里的常量
摘要:相信不少同学在见过千奇百怪的常量类之后都有这样的疑问——怎么管理这些常量类?
本文分享自华为云社区《编程实战:如何管理代码里的常量》,原文作者:技术火炬手。
相信不少同学在见过千奇百怪的常量类之后都有这样的疑问——怎么管理这些常量类,但同时又觉得这个好像不是很重要,怎么管理都能用的样子,不就是个常量嘛,今天我们就把这个问题打开来看看。
先看看前辈们是怎么做的
从事 Web 开发,有个绕不开的常量就是 HTTP 的状态码,不如以此为起点
org.springframework.http.HttpStatus
①使用枚举
org.eclipse.jetty.http.HttpStatus
②类中定义常量
org.apache.hc.core5.http.HttpStatus
③final 类中定义常量
以上都是在常用开源框架中的定义,在业务中还曾见过一种用法,是
④在接口中定义常量
这种接口被称为常量接口,不过在《EffectiveJava》中的“接口只用于定义类型”这条建议中,特别
批评了这种常量接口,认为“常量接口模式是对接口的不良使用”。原文叙述如下:
Theconstant interface pattern is a poor use of interfaces. That a class uses someconstants internally is an implementation detail. Implementing a constantinterface causes this implementation detail to leak into the class’s exportedAPI. It is of no consequence to the users of a class that the class implementsa constant interface. In fact, it may even confuse them. Worse, it represents acommitment: if in a future release the class is modified so that it no longerneeds to use the constants, it still must implement the interface to ensurebinary compatibility. If a nonfinal class implements a constant interface, allof its subclasses will have their namespaces polluted by the constants in theinterface.
常量接口模式是使用接口的糟糕方式。类内部会使用一些常量,这是实现细节。然而,实现常量接口会导致这个实现细节泄漏到类的导出 API 中。对于类的用户来说,类实现一个常量接口没有什么价值。事实上,这甚至会让他们感到困惑。更糟糕的是,它代表了一种承诺:如果在将来的版本中修改了类,使其不再需要使用常量,那么它仍然必须实现接口以确保二进制兼容性。如果一个非 final 类实现了一个常量接口,那么它的所有子类的命名空间都会被接口中的常量所污染。
考虑到常量类不应该被继承,使用 final 要比没有合适。
此外考虑到常量类应该不需要被实例化,将构造函数设置为 private 也是种额外的保护。
综上,使用枚举和 final 常量类就是首选了,那么这两者之间又如何抉择呢?我们还是能从《EffectiveJava》得到有效的建议,第34条中就是在建议我们用枚举替代 int 常量。
如何管理常量类
虽然枚举是首选,但是常量类不会消失,常量不只是 int 常量,字符串常量也是日常开发中避不开的,而且有些时候我们还是会觉得常量更为便捷。那该如何管理这些常量呢?
常量的坏味道
1. 巨幅常量类
把程序中所有用到的常量,都集中放到一个 Constants 类中。这样做会有一些缺点
1. 为了增加常量会导致这个类的修改会很频繁
2. 依赖这个常量类的代码会很多
3. 为了使用某一个常量,会导致引入很多无关的常量
2. 重复的常量定义
作为职场新人的时候,我常干的一件事情就是重复定义常量,因为我不知道别人是否定义过,在哪里定义过,与其要翻遍整个代码库去找这个常量,自己定义一个显得方便的多。
直到有一天我需要去修改一个常量的定义,前面欠下的债就需要还了。重复代码最大的问题就是,当你需要去修改或者维护那段重复的代码的时候,你需要去修改每一处,如果有遗漏,那就是 bug 了。重复常量也是如此。
管理思路
1. 定义多个功能单一的常量类
一个思路是按功能维度,去定义常量,一个常量类只跟某一功能相关。
如 MySQLConstants、RedisConstants。
但这个有个很虚的概念,就是功能,一个功能可大可小,范畴可以是某一个类,也可以是一整个模块,这里没有定论,依赖的就是程序员聪明的头脑和丰富的经验了。
2. 不单独定义常量类
另外一种思路就是不单独设计常量类,常量在哪个类里面使用,就把常量定义到这个类中。
比如在 RedisClient 和 RedisConfig 中都用到了之前 RedisConstants 中的常量,可以将其分别定义在
RedisClient 和 RedisConfig 中。
但这样做有个明显的问题就是,如果多个类用到了同一个常量,如果各自定义就造成了重复,如果定义一份声明为 public,就会造成两个类之间的依赖。这样来看,在没有复用诉求的情况下,就地定义才比较可取。
3. 按层次复用
上述两种方式都没有完整的解决我们的疑惑,考虑将上述两种方式结合起来,可以得到一种按层次定义复用的方式。
1. 跨应用复用常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
2. 应用内复用常量:放置在一方库中,通常是 common 中的 constant 目录下。
3. 模块内部复用常量:即在当前子工程的 constant 目录下。
4. 包内复用常量:即在当前包下单独的 constant 目录下。
5. 类内复用常量:直接在类内部 privatestatic final 定义。
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/eaa8fb5f1d9830f5ad0332ed3】。文章转载请联系作者。
评论