Java 根据构造函数参数实例化特定的子类型

Java 根据构造函数参数实例化特定的子类型,java,polymorphism,guice,instantiation,instanceof,Java,Polymorphism,Guice,Instantiation,Instanceof,我有四种类型的数据对象: class DataTypeAlpha extends DataType class DataTypeBeta extends DataType class DataTypeGamma extends DataType class DataTypeDelta extends DataType GUI框架中有四种不同的TreeNode类型,每种类型都特定于包装的数据类型: class AlphaTreeNode extends MyAppTreeNode ... 现在

我有四种类型的数据对象:

class DataTypeAlpha extends DataType
class DataTypeBeta extends DataType
class DataTypeGamma extends DataType
class DataTypeDelta extends DataType
GUI框架中有四种不同的TreeNode类型,每种类型都特定于包装的数据类型:

class AlphaTreeNode extends MyAppTreeNode
...
现在我经常有这样一种模式:我有一个DataType实例,需要一个MyAppTreeNode的新实例。我看到两种解决办法。解决方案一:

class DataType {
  // Instantiates and returns the appropriate MyAppTreeNode for this DataType
  abstract MyAppTreeNode createTreeNode();
}
解决方案二:

class MyAppTreeNode {
  static MyAppTreeNode createTreeNodeForDataType(DataType dataType) {
    if(dataType instanceOf DataTypeAlpha) return new AlphaTreeNode((DataTypeAlpha)dataType)
    else if (dataType instanceOf DataTypeBety) return new BetaTreeNode((DataTypeBeta)dataType)
    else if ...
    else if ...
    else throw new IllegalArgumentException();
  }
}
解决方案一使用多态性,更短,更“优雅”。但我更希望数据类型类不了解我使用的GUI框架。也许我甚至可以使用两种不同的GUI框架

你看到第三种解决方案了吗?我在这个问题上添加了Guice标签。也许Guice或其他依赖项注入库中有一些函数可以在这里提供帮助

浏览类似问题:

  • 当然,我会使用工厂模式来实现这一点,但在工厂内部,我仍然有一个问题

您可以使用访客启发的方法来实现这一点。通常,所有数据类型对象都有一个
accept
方法,但与正常的访问者模式相反,它不会遍历子对象,并且会返回一个值。为了避免太多混乱,让调用对象传递给
操作符
accept
,而不是
visitor
。诀窍是让
accept
操作符
返回泛型类型

因此,数据模型中的代码将类似于此

public abstract class DataType {
  public abstract <T> T accept(Operator<T> op);
}

public interface Operator<T> {
  T operateAlpha(DataTypeAlpha data);
  T operateBeta(DataTypeBeta data);
  ...
}

public class DataTypeAlpha extends DataType {
  public <T> T accept(Operator<T> op) {
    return op.operateAlpha(this);
  }
}
....
公共抽象类数据类型{
公共摘要不可接受(操作员op);
}
公共接口操作员{
T操作alpha(数据类型alpha数据);
T操作数据(数据类型Beta数据);
...
}
公共类DataTypeAlpha扩展了DataType{
公共不接受(操作员op){
返回op.operateAlpha(本);
}
}
....
在GUI中,您将拥有

public class TreeNodeFactory implements Operator<MyAppTreeNode> {
    public MyAppTreeNode operateAlpha(DataTypeAlpha data) {
      return new AlphaTreeNode(data);
    }
    ...
 }

 public class MyAppTreeNode {
   static TreeNodeFactory factory = ...;
   static MyAppTreeNode createTreeNodeForDataType(DataType dataType) {
     return dataType.accept(factory);
   }       
 }
公共类TreeNodeFactory实现运算符{
公共MyAppTreeNode operateAlpha(数据类型Alpha数据){
返回新的AlphaTreeNode(数据);
}
...
}
公共类MyAppTreeNode{
静态树分解工厂=。。。;
静态MyAppTreeNode createTreeNodeForDataType(数据类型DataType){
返回数据类型。接受(工厂);
}       
}

因此,简短而简单的答案是构造函数只能返回自己的类型。没有子类型,没有其他类,没有重用的实例,没有
null
——只有该类型的新实例。因此,您正在寻找一种在构造函数范围之外运行的解决方案。最简单也是最常见的解决方法是编写一个静态工厂方法(通常命名为
newInstance
getInstance
),它返回封闭类的任何新实例或现有实例,并且可以毫无困难地返回子类或
null

您关于解决方案1和2的观点是有效的。最好不要让数据类型意识到UI,在您的情况下(只有四种类型),我可能会选择您的解决方案2。如果您有不同类型的操作,这是GUI中的一个非常常见的要求,将混合类型放入树中,那么Bitnus的解决方案可能是值得的。(如果您只需要执行一次此类操作,则需要处理大量代码。)

如果您以某种方式期望类型计数增长,但操作永远不会增长,一种替代方法是将多态创建提取到一个单独的工厂中,它可能如下所示:

class MyAppTreeNode {
  interface Factory {
    MyAppTreeNode create(DataType type);
  }
}

class AlphaTreeNode extends MyAppTreeNode {
  static class Factory implements MyAppTreeNode.Factory {
    @Override public AlphaTreeNode create(DataType type) {
      // Remember, in an override your return types can be more-specific
      // but your parameter types can only be less-specific
      return new AlphaTreeNode((DataTypeAlpha) type);
    }
  }
}

那么你就可以制作一个地图(尽管考虑到Guava的iMaMabLabAP以获得更好的语义):


私有静态映射使用泛型和类似于泛型dao的解决方案怎么样?我喜欢这个。它优雅、类型安全、简短,我可以在数据类型上使用它做一些事情。我还是很好奇其他的解决方案。谢谢。一旦你的好奇心得到满足,记得接受你最喜欢的东西;)
private static Map<Class<?>, MyAppTreeNode.Factory> factoryMap = new HashMap<>();
static {
  factoryMap.put(DataTypeAlpha.class, new AlphaTreeNode.Factory());
  // ...
}

public static createTreeNode(DataType type) {
  return factoryMap.get(type.getClass()).create(type);
}