我正在进行模拟,我想基于GenericMissile抽象类创建一系列导弹类。我想确保所有常见的初始化都发生在GenericMissile上,这样我就可以避免将重复的代码传播到子类。有些参数对所有导弹都有相同的值,所以那些参数都在GenericMissile中。此外,大多数其他参数对所有导弹都是通用的,只是具有不同的值。最后,有一些可选参数,一些导弹可能有或没有(并且它们可以以不同的排列存在,即使对于相同的子类也是如此)。我的问题是如何在抽象类中正确链接构造函数以实现此行为。这是我试图做的模拟:
public abstract class GenericMissile {
public abstract void initSpecificParams();
private double x;
private double y;
private double z;
protected int optionalParam1 = 0;
protected int optionalParam2 = 0;
protected int optionalParam3 = 0;
public GenericMissile(double x, double y, double z) { // basic constructor with required params
this.x = x;
this.y = y;
this.z = z;
initSpecificParams(); // each missile type initializes common params that needs specialized values
}
// --------------------------------
// OPTION 1: duplicate everything from basic constructor, and add optional stuff
public GenericMissile(double x, double y, double z, int optionalParam1, int optionalParam2, int optionalParam3) {
this.x = x; // duplicating all these assignments
this.y = y;
this.z = z;
this.optionalParam1 = decode(optionalParam1); // using "decode" as a generic representation for doing unit conversion, scaling, enum-to-enum type mappings, etc.
this.optionalParam2 = decode(optionalParam2);
this.optionalParam3 = decode(optionalParam3);
initSpecificParams();
}
... // create one constructor like this for each combo of optional Params
// -------------------------------
// OPTION 2: duplicate everything from basic constructor, and add optional stuff
public GenericMissile(doublex, double y, double z, int optionalParam1) {
this(x,y,z);
initSpecificParams(optionalParam1);
}
// ... (create a GenericMissile constructor with each permutation of optionalParams, that calls the appropriate initSpecificParams(...)
//------------------------------
// OPTION 3: try to re-use the first constructor (which is tricky because of the dependence of initSpecificParams() on the optional parameters being set
public GenericMissile(double x, double y, double z, int optionalParam1, int optionalParam2, int optionalParam3) { // When a missile type uses optional parameter, it uses this constructor instead
this.optionalParam1 = optionalParam1; // I know this is illegal, but this is what I would like to do, because initSpecificParams() will check for these optional parameter values
this.optionalParam2 = optionalParam2;
this.optionalParam3 = optionalParam3;
this(x,y,z); // not on the first line :(
}
}
public class MissileA extends GenericMissile {
public MissileA(double x) {
super(x);
}
// Note: three constructors with optional params, all re-using the same GenericMissile constructor (good!), which in turn calls the sub-class implementation of initSpecificParams()
public MissileA(double x, double y, double z, int optionalParam1) {
super(x, y, z, optionalParam1, optionalParam2, optionalParam3);
}
public MissileA(double x, double y, double z, int optionalParam1, int optionalParam2) {
super(x, y, z, optionalParam1, optionalParam2, optionalParam3);
}
public MissileA(double x, double y, double z, int optionalParam1, int optionalParam2, int optionalParam3) {
super(x, y, z, optionalParam1, optionalParam2, optionalParam3);
}
// --------------------------
// OPTION 1:
// Ideally, I would be able to set any optional parameters in the Generic constructor, and then use the same initSpecificParams() method regardless of which
// optional parameters are being used - no duplication
public void initSpecificParams() {
if (optionalParam1 != 0 ) {
readSpecificParameterFile1(optionalParam1);
} else {
readDefaultParameterFile();
}
if (optionalParam2 != 0 ) {
readSpecificParameterFile2(optionalParam2);
} else {
readDefaultParameterFile();
}
if (optionalParam3 != 0) {
readSpecificParameterFile3(optionalParam3);
} else {
readDefaultParameterFile();
}
do_common_stuff(); // NOTE: this common initialization depends on which parameter files are loaded in the previous if-else blocks
}
// -----------------------------
// OPTION 2: include these extra initSpecificParams() methods
// If I cannot set optional params in GenericMissile constructor in front of another constructor call, I have to build separate initSpecificParams() methods to explictly
// pass in the optional params, instead of just setting them as instance variable values -- lots of duplication in having to do that
public void initSpecificParams(int optionalParam1) {
this.optionalParam1 = optionalParam1;
initSpecificParams(); // NOTE: no way to force subclasses to chain these initialization methods properly
}
// Significant duplication
public void initSpecificParams(int optionalParam1, int optionalParam2) {
this.optionalParam1 = optionalParam1;
this.optionalParam2 = optionalParam2;
initSpecificParams(); // NOTE: no way to force subclasses to chain these initialization methods properly
}
// Significant duplication
public void initSpecificParams(int optionalParam1, int optionalParam2, int optionalParam3) {
this.optionalParam1 = optionalParam1;
this.optionalParam2 = optionalParam2;
this.optionalParam3 = optionalParam3;
initSpecificParams(); // NOTE: no way to force subclasses to chain these initialization methods properly
}
}
似乎我被迫要么:
在我不必复制如此多的代码的情况下,是否有一些更干净的方法可以做到这一点?注意:我已经缩短了这个例子......实际上,存在大量的通用参数,具有不同值的公共参数和可选参数。这里有很多SLOC。
答案 0 :(得分:1)
在这种情况下,您可以通过其他方式链接构造函数:
public GenericMissile(double x) {
this(x, "");
}
public GenericMissile(double x, String optionalParam) {
this.x = x;
this.optionalParam = optionalParam;
initSpecificParams();
}
但通常这种多步骤初始化是一种代码气味(正如Jon Skeet在评论中所说,你可能不应该从构造函数中进行虚拟方法调用)。
从您的示例中很难说,但使用composition instead of inheritance可能会更好,也许应用Strategy pattern。