我希望一堆子类在完成构造函数后调用超级方法,如下所示:
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()
是一个不好的概念。有没有更优雅的方法来解决这个问题?或者我应该彻底重新思考我的概念吗?
您的请求违反了多项 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()
方法是否被调用。您可以添加一个标志,每次在对象上调用方法时都会检查该标志。但确实很麻烦而且容易出错。避免这种情况。使用包装纸,它可能看起来像:
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
方法来
抽象超类的使用,该方法将执行您想要的代码,但也会检查它是否已正确设置:
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(...));
尽管这主要是一种反模式,应该谨慎使用。
我遇到了同样的问题,工厂强制执行额外的初始化逻辑
internal class ExpenseFactory
{
internal static Expense CreateExpense(ExpenseArguments args)
{
Expense? expense = null;
switch (args.ExpenseType)
{
case ExpenseType.EQUAL:
expense = new EqualExpense(Guid.NewGuid().ToString(), args.Amount, args.PaidBy, args.SharedBy);
break;
case ExpenseType.EXACT:
expense = new ExactExpense(Guid.NewGuid().ToString(), args.Amount, args.PaidBy, args.SharedBy, args.ExactAmounts);
break;
case ExpenseType.PERCENT:
expense = new PercentExpense(Guid.NewGuid().ToString(), args.Amount, args.PaidBy, args.SharedBy, args.Percentages);
break;
default:
throw new ArgumentException("Ivalid expense arguments");
}
expense.InitSplits(); // additional init logic implemented by each subclass and declared as abstract in parent class Expense
return expense;
}
}