我正在寻找一段代码,其行为有点像单身但不是(因为单身人士很糟糕:)我正在寻找的必须达到以下目标:
线程安全
简单(理解和使用,即几行代码.库调用正常)
快速
不是单身人士; 对于测试,必须可以覆盖该值(并在测试后重置它).
本地(所有必要的信息必须在一个地方)
懒惰(仅在实际需要值时运行).
运行一次(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配置文件等).
[编辑]我想知道为什么这么多人投票.这是一个有效的问题,用例很常见(至少在我的代码中).所以,如果你不理解这个问题(或背后的原因),或者你没有答案或者你不关心,为什么还要投票呢?:/
[编辑2]如果你看看Spring的应用程序上下文,你会发现超过99%的bean只有一个实现.你可以拥有更多,但在实践中,你根本就没有.因此,我不是分离接口,实现和配置,而是查看只有一个实现(在最简单的情况下),current()方法和一行或两行聪明的代码来初始化current()的结果的东西.一次(当它第一次被调用时)但同时允许覆盖结果(线程安全,如果可能的话).可以把它想象成一个原子"if(o == null)o = new O(); return o"你可以覆盖o.也许AtomicRunOnceReference类是解决方案.
现在,我只是觉得我们每天都拥有和使用的东西并不是最佳的,有一个令人困惑的解决方案会让我们大家都低头说"就是这样".就像我们几年前Spring出现时所感受到的那样,我们意识到我们所有单身人士问题的来源以及如何解决这些问题.
线程安全初始化代码(imho)的最佳习惯是懒惰的内部类.经典版本是
class Outer { class Inner { private final static SomeInterface SINGLETON; static { // create the SINGLETON } } public SomeInterface getMyObject() { return Inner.SINGLETON; } }
因为它是线程安全,懒惰加载和(imho)优雅.
现在您需要可测试性和可替换性.很难在不知道究竟是什么的情况下提出建议,但最明显的解决方案是使用依赖注入,特别是如果你正在使用Spring并且无论如何都有应用程序上下文.
这样你的"单身人士"的行为就会被一个界面所代表,你只需将其中一个注入你的相关课程(或工厂生产一个),然后你可以用你喜欢的任何东西替换它来进行测试.