2

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

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 或其他依赖注入库中有一些函数可以在这里提供帮助?

浏览类似的问题:

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

2 回答 2

1

您可以为此使用受访客启发的方法。像往常一样,所有 DataType 对象都有一个accept方法,但与正常的访问者模式不同,它不遍历子对象,而是返回一个值。为避免过多的混淆,让我们调用传递给的对象acceptfor anoperator而不是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);
   }       
 }
于 2013-06-21T10:29:46.777 回答
0

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

您关于解决方案 1 和 2 的观点是有效的。最好避免让数据类型知道 UI,在您的情况下(只有四种类型),我可能会选择您的解决方案 2。如果您的操作在这些类型之间会有所不同 - 这是在将混合类型放入树的 GUI 中非常常见的需求——Bittenus 的解决方案可能是值得的。(如果您只需要执行一次此类操作,则需要处理很多代码。)

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

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 实现的能力,但您仍然需要映射DataTypeMyAppTreeNode.Factory一种或另一种方式,并且它必须存在于为访问者提供支持的 Map、条件或双重间接中图案。

希望这会有所帮助,如果只是为了认可您已经拥有的答案!

于 2013-06-24T03:38:40.053 回答