我正在寻找一段代码,它的行为有点像单例但不是(因为单例很糟糕:) 我正在寻找的必须满足这些目标:
- 线程安全
- 简单(理解和使用,即几行代码。库调用是可以的)
- 快速地
- 不是单身;对于测试,必须可以覆盖该值(并在测试后将其重置)。
- 本地(所有必要信息必须在一个地方)
- 惰性(仅在实际需要该值时运行)。
- 运行一次(RHS 上的代码必须执行一次且仅一次)
示例代码:
private int i = runOnce(5); // Set i to 5
// Create the connection once and cache the result
private Connection db = runOnce(createDBConnection("DB_NAME"));
public void m() {
String greet = runOnce("World");
System.out.println("Hello, "+greet+"!");
}
请注意,这些字段不是静态的;只有表达式的 RHS(右手边)是……在某种程度上是“静态的”。测试应该能够临时注入新i
值greet
。
另请注意,这段代码概述了我打算如何使用这个新代码。随意用任何东西替换 runOnce() 或将其移动到其他地方(构造函数,也许,或 init() 方法或 getter)。但是 LOC 越少越好。
一些背景资料:
我不是在寻找 Spring,而是在寻找一段可用于最常见情况的代码:您需要实现一个接口,除了您想要的测试之外,永远不会有第二个实现传入模拟对象。此外,Spring 失败了 #2、#3 和 #5:您需要学习配置语言,您必须在某处设置应用程序上下文,它需要 XML 解析器并且它不是本地的(信息遍布各处)。
由于#5,全局配置对象或工厂不符合要求。
static final
因为#4而出局(不能改变决赛)。static
由于类加载器问题而有异味,但您可能需要在里面使用它runOnce()
。我只是希望能够在表达式的 LHS 中避免它。
一种可能的解决方案可能是将ehcache与默认设置一起使用,该设置将返回相同的对象。由于我可以将内容放入缓存中,因此这也允许随时覆盖该值。但也许有比 ehcache 更紧凑/简单的解决方案(它再次需要 XML 配置文件等)。
[编辑] 我想知道为什么这么多人不赞成这个。这是一个有效的问题,用例很常见(至少在我的代码中)。因此,如果您不理解问题(或其背后的原因),或者您没有答案或不在乎,为什么要投反对票?:/
[EDIT2] 如果您查看 Spring 的应用程序上下文,您会发现超过 99% 的所有 bean 都只有一个实现。你可以拥有更多,但在实践中,你根本没有。因此,我没有分离接口、实现和配置,而是在寻找只有一个实现(在最简单的情况下)、一个 current() 方法和一两行聪明的代码来初始化 current() 的结果的东西一次(第一次调用时)但同时允许覆盖结果(如果可能,线程安全)。把它想象成一个原子的“if(o==null) o = new O(); return o”,你可以在其中覆盖 o。也许 AtomicRunOnceReference 类是解决方案。
现在,我只是觉得我们每天都拥有和使用的并不是最佳的,有一个莫名其妙的解决方案会让我们都拍脑袋说“就是这样”。就像我们几年前 Spring 出现时的感觉一样,我们意识到我们所有的单例问题来自哪里以及如何解决它们。