我在找一块代码,它的行为就像一个单独但并不是(因为单一的坏)我在寻找什么必须满足这些目标:

  1. 线安全
  2. 简单(理解和使用,即几行的代码。图书馆电话被确定)
  3. 快速
  4. 没有一个单一实例;用于测试,它必须能够复盖值(和重置它之后的试验)。
  5. 当地(所有必要的信息必须在同一个地方)
  6. 懒惰的(运行只有当值实际上需要)。
  7. 运行一次(代码右轴必须执行一次只有一次)

例编码:

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+"!");
}

注意的领域是 静态的;只有右轴(右侧)的表达是...好的"静态"对某一程度。试验应当能够注入新的价值观为 igreet 暂时的。

还注意到,这段代码概述了我是如何打算使用这种新的编码。随替代runOnce()用任何东西,或将其移至其他一些地方(本构造,也许,或者一个初始化()法或吸气).但小LOC好。

一些背景信息:

我不是在寻找春天,我在找一块代码可以使用的最常见的情况:你需要实现一个接口,并且有永远不会是第二次执行情况,除了用于测试在那里你想要通过在模拟对象。此外,弹簧失败#2,#3和第5:你需要学习的配置语言,则必须设置的应用程序方面的某个地方,它需要一种XML parser而这是不当地(信息传播到地方)。

全球配对象或工厂无法满足该法案,因为#5.

static final 是因为第4(不能改变最终). static 闻起来,因为类装入器的问题,但是你可能会需要它的内部 runOnce().我只是喜欢能够避免它在左轴的表达。

一个可能的解决办法可能是使用 有一种默认设置这将返回相同的对象。因为我可以把东西放在高速缓存,这也将允许以替代的价值在任何时间。但也许有一个更紧凑、简单的解决方案比里(这再次需要一个XML配置文件等)。

[编辑]我不知道为什么这么多人的列表这一点。这是一个有效的问题和使用情况是很常见的(至少在我的代码)。所以如果你不了解这个问题(或它背后的原因)或者如果你没有回答或者你不在乎为什么列表?:/

[EDIT2]如果你看的应用程序方面的春天,你会发现,超过99%的所有豆只有一个单一的执行。你能有更多,但在实践中,你根本没有。因此,而不是分离接口的实施和配置,我在看一些东西,只有执行(在最简单的情况下),当前()方法和一个或两个线的聪明的代码来初始化的结果对于当前的()进行一次(在第一时间)但在同一时间允许复盖的结果(线的安全,如果可能)。认为它作为一个原子"如果(o==null)o=new O();回o"在那里你可以改写。也许一个AtomicRunOnceReference类的解决方案。

现在,我只是觉得那什么,我们所有拥有和使用每日不是最佳的,有一个令人困惑的解决办法,这将使我们所有人打我们的元首,并说"这是它。"只是因为我们认为,当春天来了围绕几年前,我们意识到我们所有的单独问题来自何处和如何解决这些问题。

有帮助吗?

解决方案 2

这里是一个解决方案,满足我所有的要求:

/** Lazy initialization of a field value based on the (correct)
* double checked locking idiom by Joschua Bloch
*
* <p>See "Effective Java, Second Edition", p. 283
*/
public abstract class LazyInit<T>
{
    private volatile T field;

    /** Return the value.
    *
    *  <p>If the value is still <code>null</code>, the method will block and
    *  invoke <code>computeValue()</code>. Calls from other threads will wait
    *  until the call from the first thread will complete.
    */
    @edu.umd.cs.findbugs.annotations.SuppressWarnings("UG_SYNC_SET_UNSYNC_GET")
    public T get ()
    {
        T result = field;
        if (result == null) // First check (no locking)
        {
            synchronized (this)
            {
                result = field;
                if (result == null) // Second check (with locking)
                {
                    field = result = computeValue ();
                }
            }
        }
        return result;
    }

    protected abstract T computeValue ();

    /** Setter for tests */
    public synchronized void set (T value)
    {
        field = value;
    }

    public boolean hasValue()
    {
        return field != null;
    }
}

其他提示

最好的成语线程安全的初始化时代码(恕我直言)是懒内类。经典的版本是

class Outer {
  class Inner {
    private final static SomeInterface SINGLETON;

    static {
      // create the SINGLETON
    }
  }

  public SomeInterface getMyObject() {
    return Inner.SINGLETON;
  }
}

因为这是线程安全的,懒惰的装载和(恕我直言)优雅。

现在你想要的可测性和可更换性.很难劝不知道它究竟是什么但最明显的解决办法是使用依赖注射,特别是如果您使用的弹簧和有一个应用程序方面。

这样你的"单独"'s行为是由一个接口,你只需注入一些到你的相关类别(或一家工厂来生产一个)和然后你当然可以替代它与任何你喜欢的用于测试目的。

你可以使用IoC技术,甚至如果你不使用IoC框架(春题之一,因/...).这是在我看来,只有清洁的方式,以避免一个单独的.

我觉得一个解决方案可以使用是提供保护的方法,它可能复盖在测试(一个解决方案我使用之前进行测试的遗产代码)。

所以是这样的:

private SomeObject object;

protected SomeObject getObject() {
   if (object == null) {
       object = new SomeObject();
   }
   return object;
}

然后在你的测试类可以做到:

public void setUp() {
   MyClassUnderTest cut = new MyClassUserTest() {
      @Override
      protected SomeObject getObject() }
         return mockSomeObject;
      }
   };
}

我必须说我是不是过分热衷于这种模式,因为它使受保护的领域在那里你不会真的想要它,但它是有用的你的一些情况下,注不是一个选项

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top