假设我有一个Java类
abstract class Base {
abstract void init();
...
}
Run Code Online (Sandbox Code Playgroud)
而且我知道每个派生类都必须init()在它构造之后调用.当然,我可以简单地在派生类的构造函数中调用它:
class Derived1 extends Base {
Derived1() {
...
init();
}
}
class Derived2 extends Base {
Derived2() {
...
init();
}
}
Run Code Online (Sandbox Code Playgroud)
但这会破坏"不要重复自己"的原则而且会很糟糕(而且会有很多子类Base).当然,init()调用不能进入Base()构造函数,因为它会被执行得太早.
任何想法如何绕过这个问题?我也很高兴看到Scala解决方案.
更新:这是工厂方法方法的通用版本:
interface Maker<T extends Base> {
T make();
}
class Base {
...
static <T extends Base> T makeAndInit(Maker<T> maker) {
T result = maker.make();
result.init();
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
更新2:这个问题基本上是"你如何使用模板方法为构造函数"?答案似乎是,"你可以,但这是一个坏主意".所以我可以做一个模板工厂(模板方法+抽象工厂).
Boz*_*zho 11
避免这样做.如果你这样做,任何扩展你的DerivedX类的类都可能决定也调用,init()从而使对象处于不一致状态.
一种方法是让init()您的类的客户端手动调用该方法.有一个initialized字段,IllegalStateExcepion如果没有它就调用任何需要初始化的方法.
更好的方法是使用静态工厂方法而不是构造函数:
public Derived2 extends Base {
public static Derived2 create() {
Derived2 instance = new Dervied2();
instance.init();
return instance;
}
}
Run Code Online (Sandbox Code Playgroud)
更新:正如您在更新中建议的那样,您可以将Builder传递给静态工厂方法,该方法将调用init()实例.如果您的子类很少,我认为这是一个过度复杂的.
eri*_*son 10
会发生什么init()?更好的设计可能会完全消除该方法,或者至少放松它在子类的构造函数之后执行的要求.可以肯定,init()不会使在建的对象可见的构造完成前的任何其他线程,因为它创建并发错误.
作为(丑陋的)替代方案,抽象方法可以通过子类作为伪构造函数来实现:
abstract class Base {
Base() {
ctor();
init();
}
abstract void ctor();
abstract void init();
}
Run Code Online (Sandbox Code Playgroud)
除了Bozho的推荐之外,应用程序容器非常适合这项任务.
init()使用javax.annotation.PostConstruct注释标记您的方法,并且正确配置的EJB或Spring容器将在依赖注入完成之后但在应用程序可以使用该对象之前执行该方法.
一个示例方法:
@PostConstruct
public void init() {
// logic..
}
Run Code Online (Sandbox Code Playgroud)
在企业应用程序中,您可以打开资源,例如方法中的文件系统init().此初始化可以抛出异常,不应该从构造函数中调用.