我希望一堆子类在完成如下构造函数后调用一个超级方法:
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()
必须是子构造函数的第一行,因此在调用超级构造函数之前,我无法设置任何变量。
它现在可以正常工作,但是我发现在每个子类的构造函数的末尾调用dispatch()
是一个不好的想法。有没有更优雅的方式来解决这个问题?还是我应该完全重新考虑我的概念?
答案 0 :(得分:4)
您的请求违反了几种Java最佳做法,例如:
请勿在构造函数中进行复杂的配置,仅填充私有(最终)成员变量,并且仅执行非常基本的一致性检查(如果有的话)。
请勿从构造函数中调用non private
或non final
方法,甚至不能间接调用。
因此,我强烈建议您考虑一下类的设计。可能是,您的班级太多,并且有太多的责任感。
答案 1 :(得分:2)
它现在可以正常工作,但是我觉得打电话给他一个不好的想法 每个子类的构造函数结尾处的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...
}
答案 2 :(得分:0)
Lorelorelore是正确的,如果在调用该方法时确实需要完成任何子类实例化。否则,您可以做您所拥有的。如果其他人需要使用该代码,我建议提出足够的注释。
答案 3 :(得分:0)
您可以通过提供一种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(...));
尽管这主要是一种反模式,应该备用。