Java - 摘要中的链接构造函数 Class

Java - Chaining Constructors in Abstract Class

我正在进行模拟,我想根据 GenericMissile 摘要 class 创建一个导弹家族 classes。我想确保所有常见的初始化都发生在 GenericMissile 上,这样我就可以避免将重复的代码传播到 subclasses。某些参数对于所有导弹具有相同的值,因此这些参数位于 GenericMissile 中。此外,大多数其他参数对于所有导弹都是通用的,只是具有不同的值。最后,有一些导弹可能有也可能没有的一些可选参数(它们可以以不同的排列存在,即使对于相同的 subclass 也是如此)。我的问题是关于如何正确链接抽象 class 中的构造函数以实现此行为。这是我正在尝试做的事情的模型:

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
 }

}

看来我是被迫的:

  1. 复制两个 GenericMissile 构造函数中的所有代码(使用 第二个中的其他参数设置),或
  2. 在每个 subclass 中复制 initSpecificParams() 方法并确保它们链接在一起(initSpecificParams(optional_param) 必须调用 initSpecificParams()).

有没有一些更简洁的方法可以做到这一点而不必重复那么多代码?注意:我已经缩短了这个例子……实际上有大量的通用参数、具有不同值的通用参数和可选参数。这里有很多 SLOC。

在这种情况下,您可以简单地以另一种方式链接构造函数:

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, perhaps by applying the Strategy pattern 可能会更成功。