Clojure实现UrlValidator仅单次求值的最优写法选择
三种写法的优劣势对比
- 普通
def:
每次命名空间加载、代码重载时都会重新执行求值逻辑生成新的UrlValidator实例。生产环境如果没有热重载逻辑可以正常运行,但开发阶段反复重载命名空间时会重复生成实例,不符合「仅求值一次」的要求。 - 带
^:const元数据的def:^:const的作用是告知编译器该值为编译期常量,编译阶段会把所有引用该变量的位置直接替换为常量值。但UrlValidator是运行时生成的对象实例,不属于编译期可确定的基础类型常量,加这个元数据没有实际意义,反而可能导致热重载时引用处的值不更新,出现预期外的问题,且它和普通def一样会在重载时重复初始化。 defonce:
仅在对应变量从未被定义过时才会执行后面的初始化逻辑,只要变量已经存在,不管后续重载多少次命名空间都不会重新生成实例,完全符合「逻辑仅执行一次、后续返回已有结果」的需求,开发、生产环境都适用,所以官方文档会推荐这个方案。
其他可选实现方式
如果你的需求是「首次调用时才初始化,而非命名空间加载时就初始化」的惰性单例,可以选择下面两种方案:
- 用
delay实现懒加载:
(def get-validator (delay (UrlValidator. (into-array ["https"]))))
使用时通过@get-validator解引用即可,第一次解引用时才会执行实例化逻辑,后续所有解引用都返回同一个实例。
2. 用memoize实现函数式单例:
(def get-validator (memoize (fn [] (UrlValidator. (into-array ["https"])))))
保留了你原来的函数调用形式,第一次调用(get-validator)时生成实例,后续所有调用都直接返回缓存的实例。
内容的提问来源于stack exchange,提问作者Lars Melander
相关产品推荐
相关产品推荐

