[英]Java - Force subclasses to call super method after constructor
我希望一堆子类在完成如下构造函数后调用一个超级方法:
public abstract class Superclass {
...
public Superclass(...) {
... // do stuff before initializing subclass
}
protected void dispatch() { //method to be called directly after creating an object
doStuff();
...
}
public abstract void doStuff();
}
public class Subclass extends Superclass {
...
public Subclass(...) {
super(...); //has to be the first line
... //assign variables etc.
dispatch(); //has to be called after variables are assigned etc.
}
public void doStuff() {
//do stuff with assigned variables etc.
}
}
dispatch()
函数包含对象创建后的一系列操作,该对象必须应用于所有子类。 我不能将此函数移到超级构造函数中,因为它从需要已分配变量的子类中调用方法。 但是由于super()
必须是子构造函数的第一行,因此在调用super构造函数之前,我无法设置任何变量。
它现在可以正常工作,但是我发现在每个子类的构造函数的末尾调用dispatch()
是一个不好的概念。 有没有更优雅的方式来解决这个问题? 还是我应该完全重新考虑我的概念?
您的请求违反了几种Java最佳实践,例如:
不要在构造函数中进行复杂的配置,仅填充私有(最终)成员变量,并且仅执行非常基本的一致性检查(如果有的话)。
不要从构造函数中调用non private
non final
方法,甚至不能间接调用。
因此,我强烈建议您考虑一下类的设计。 很有可能您的班级太多且有太多的责任感。
它现在可以正常工作,但是我发现在每个子类的构造函数的末尾调用dispatch()是一个不好的概念。 有没有更优雅的方式来解决这个问题? 还是我应该完全重新考虑我的概念?
正如Timothy Truckle所强调的那样,您的构造函数逻辑过于复杂。
通过使用模板方法初始化子类实例,可以使事情变得更简单并达到目标。 请注意,您已经在doStuff()
使用了此模式。
子类构造函数的确是您遇到的问题:您希望减少每个子类中所需的强制性样板,并使它们的可读性和维护性更好。
因此,在超类中引入一个新的模板方法,并从超类的构造函数中调用它。
此方法与构造函数的作用相同,但可以通过更灵活的方式调用。
也是一种人工方法的dispatch()
,也不需要仅为该技巧引入。
整个逻辑可以从超类构造函数进行编排。
超级类可能看起来像:
public abstract class Superclass {
...
public Superclass(...) {
... // do stuff before initializing subclass
init();
doStuff();
}
public abstract void init();
public abstract void doStuff();
}
并在子类中,替换为:
public Subclass(...) {
super(...); //has to be the first line
... //assign variables etc.
dispatch(); //has to be called after variables are assigned etc.
}
创建人:
public Subclass(...) {
super(...); // let the super constructor to orchestrate the init logic
}
public void init(){
// move the constructor logic here
}
结果要简单得多,因为此设计可以在一个地方(即超类构造函数)集中收集与子类的初始化“算法”相关的职责。
关于您的评论:
这确实比我做的还要优雅。 谢谢! 编辑:刚注意到,这不适用于具有不同构造函数参数的子类。 任何想法如何解决这个问题?
出于这样的要求,为了使事情变得简单明了,您必须分两步进行:
init()
方法。 它可能看起来像:
SuperClass o = new Subclass(argFoo, argBar);
o.init();
这种方法的问题在于您不确定是否调用了init()
方法。 您可以添加一个标志,每次在对象上调用方法时都要检查该标志。 但这确实很麻烦且容易出错。 避免那样。
为了改善这一点,我可能会使用包装模式。
您也可以使用拦截器/方面。 但这不是一个好用例:init处理不是横向的,实际上与对象行为有关。 使其可见更有意义。
使用包装器,它看起来可能像:
SuperClass o = new MyWrapper(new Subclass(argFoo, argBar));
其中MyWrapper
是的子类, SuperClass
和包装的一个实例SuperClass
对象:
public class MyWrapper implements SuperClass{
private SuperClass wrapped;
public MyWrapper (SuperClass wrapped){
this.wrapped = wrapped;
this.wrapped.init();
}
// then delegate each superclass method to the wrapped object
public void doStuff(){
this.wrapped.doStuff();
}
// and so for...
}
如果您确实需要在调用该方法时完成任何子类实例化,那么Lorelorelore是正确的。 否则,您可以做您所拥有的。 如果其他人需要使用该代码,我建议提出足够的注释。
您可以通过提供一个static
方法来抽象您对SuperClass的使用,该方法将执行您想要的代码,但还可以检查它是否已正确设置:
public abstract class SuperClass{
private boolean instantiated;
public SuperClass(...){
...
}
public abstract void doStuff();
private void dispatch(){
if(!instantiated){
instantiated = true;
doStuff();
}
}
public static void executeActionOnSuperClass(SuperClass s){
s.dispatch(); // call instantiation if not already done
s.executeAnAction();
}
}
和子类:
public class SubClass extends SuperClass{
public SubClass(...){
super(...);
}
public void doStuff(){
...
}
}
然后可以这样执行:
SuperClass.executeAnActionOnSuperClass(new SubClass(...));
虽然这主要是一种反模式,但应该备用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.