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

时间:2013-06-21 07:56:49

标签: java polymorphism guice instantiation instanceof

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

class DataTypeAlpha extends DataType
class DataTypeBeta extends DataType
class DataTypeGamma extends DataType
class DataTypeDelta extends DataType

和GUI框架中的四种不同的TreeNode类型,每种类型都特定于包装的DataType:

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();
  }
}

解决方案一使用多态,更短,更“优雅”。但我更喜欢DataType类不了解我使用的GUI框架。也许我甚至可以使用两个不同的GUI框架?

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

查看类似的问题:

  • 当然我会使用工厂模式,但在工厂内我仍然有问题。

2 个答案:

答案 0 :(得分:1)

您可以使用受访者启发的方法。像往常一样,所有DataType对象都有accept方法,但与普通访问者模式相反,它不会遍历子节点,它将返回一个值。为了避免太多混淆,让调用对象传递给accept而不是operator而不是visitor。诀窍是让acceptoperators返回泛型类型。

所以代码在数据模型中会是这样的

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);
  }
}
....

在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);
   }       
 }

答案 1 :(得分:0)

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

您对解决方案1和2的要点是有效的。最好避免使数据类型知道UI,并且在您的情况下(只有四种类型)我可能会选择您的解决方案2.如果您的操作因这些类型而异 - 这是一个GUI中非常常见的要求将混合类型放入树中 - Bittenus的解决方案可能是值得的。 (如果你只需要做一次这样的事情就可以处理很多代码。)

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

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的ImmutableMap以获得更好的语义):

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);
}

比它的价值更麻烦? 可能在大多数情况下。但请记住,Guice可能也是最好的。 Guice有能力为您自动生成Factory实现,但您仍然需要以这种或那种方式将DataType映射到MyAppTreeNode.Factory,并且它必须存在于Map中,条件,或为访客模式提供动力的双重间接。

希望这有帮助,只要赞同你已经拥有的答案!