单例对象初始化和依赖注入

SIngleton object initialization and dependency injection

我通常使用这样的单例持有者模式创建单例 类:

public class Foo {

    private static final class SingletonHolder {
        private static final Foo INSTANCE = new Foo();
    }  

    private Foo(){}

    public static final Foo getInstance(){
        return SingletonHolder.INSTANCE;
    }

}

一切正常。但是,如果我需要注入一个依赖来初始化单例对象呢?

在那种情况下,我添加了一个方法 initialize 来接收依赖项并且必须只调用一次:

public class Foo {

    private static final class SingletonHolder {
        private static final Foo INSTANCE = new Foo();
    }  

    private Foo(){}

    public static final void initialize(Dependency d) {
        SingletonHolder.INSTANCE.setDependency(d);
    }

    public static final Foo getInstance(){
        return SingletonHolder.INSTANCE;
    }

}

我走对了吗?还有其他解决方案吗?我知道这取决于程序,我的逻辑等等......但是一般应该如何解决这个问题?

这种方法的问题在于 initialize 方法可以被多次调用,并且您的代码中没有任何迹象表明正在使用线程安全处理设置依赖关系。

如果您对此没有意见,那么您的惰性初始化惯用法就可以了。

否则,当在单例实例中多次设置依赖项时,您可以抛出 IllegalStateException 或以其他方式静默不执行任何操作。

编辑

正如 Andy Thomas 所说,您也没有检查在调用 getInstance 之前是否设置了依赖项,或者至少您的代码没有显示它。

我认为你过于复杂了。在我工作过的几个地方(包括我现在工作的地方),我们不尝试强制使用单身人士。实际应用程序的所有连接都在一个地方完成,因此如果您搜索构造函数的用法,您应该在 src 中找到一个,并且可能在 test 中找到多个。请参阅下面的代码。

您的方法的一些缺点:

  • 你失去了不变性,Foo的依赖可以改变
  • setDependencyinitialize 方法都是生产中的测试代码
  • 您的构造函数没有创建有效对象,构造函数的后半部分在 initialize 方法中,您必须记住在调用构造函数后调用该方法
  • SingletonHolder 是样板代码,我不确定你为什么不声明 public static final Foo instance 字段呢?
  • 仍然可以使用反射 API 和对象序列化机制
  • 创建 Foo 的多个实例
public class Foo {

    private final Dependency dependency;

    public Foo(Dependency dependency) {
        this.dependency = dependency;
    }

    // ...
}

public class Dependency {
    // ...
}