有几种不同的方法可以初始化复杂的对象(注入的依赖关系和注入成员的所需设置),这些都看起来很合理,但有各种各样的优点和缺点.我会举一个具体的例子:
final class MyClass { private final Dependency dependency; @Inject public MyClass(Dependency dependency) { this.dependency = dependency; dependency.addHandler(new Handler() { @Override void handle(int foo) { MyClass.this.doSomething(foo); } }); doSomething(0); } private void doSomething(int foo) { dependency.doSomethingElse(foo+1); } }
你可以看到,构造函数做了3件事情,包括调用一个实例方法.我被告知,从构造函数调用实例方法是不安全的,因为它规避了未初始化成员的编译器检查.即我可以在设置this.dependency之前调用doSomething(0),这将被编译但没有工作.什么是最好的方式来重构这个?
>使doSomething静态并显式传递依赖关系在我实际的情况下,我有三个实例方法和三个成员字段,它们都相互依赖,所以这似乎是很多额外的样板,使所有这三个静态.
>将addHandler和doSomething移动到@Inject public void init()方法中.与Guice一起使用将是透明的,它需要任何手动构造来确保调用init(),否则如果有人忘记,对象将不会完全正常.此外,这暴露了更多的API,这两个似乎都是坏主意.
>包装一个嵌套类以保持依赖关系,以确保其正常运行,而不会暴露其他API:
class DependencyManager { private final Dependency dependency; public DependecyManager(Dependency dependency) { ... } public doSomething(int foo) { ... } } @Inject public MyClass(Dependency dependency) { DependencyManager manager = new DependencyManager(dependency); manager.doSomething(0); }
这将实例方法从所有构造函数中抽出,但是会生成一个额外的类,当我已经拥有内部和匿名类(例如处理程序)时,可能会变得混乱 – 当我尝试这一点时,被告知要将DependencyManager移动到单独的文件,这也是令人讨厌的,因为它现在是多个文件做一件事情.
那么处理这种情况的首选方式是什么?
解决方法
有效Java中的Josh Bloch建议使用静态工厂方法,尽管我找不到任何参数.然而,在
Java Concurrency in Practice年有一个类似的情况,专门用于防止从构造函数中泄漏引用.适用于这种情况,它看起来像:
final class MyClass { private final Dependency dependency; private MyClass(Dependency dependency) { this.dependency = dependency; } public static createInstance(Dependency dependency) { MyClass instance = new MyClass(dependency); dependency.addHandler(new Handler() { @Override void handle(int foo) { instance.doSomething(foo); } }); instance.doSomething(0); return instance; } ... }
但是,使用DI注释可能无法正常工作.