我的情况是我有四种类型的数据对象:
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或其他依赖注入库中有一些函数可以帮助吗?
查看类似的问题:
答案 0 :(得分:1)
您可以使用受访者启发的方法。像往常一样,所有DataType对象都有accept
方法,但与普通访问者模式相反,它不会遍历子节点,它将返回一个值。为了避免太多混淆,让调用对象传递给accept
而不是operator
而不是visitor
。诀窍是让accept
和operators
返回泛型类型。
所以代码在数据模型中会是这样的
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
- 只有该类型的新实例。因此,您正在寻找一种在构造函数范围之外运行的解决方案。最简单和最常见的解决方法是编写一个静态工厂方法(通常名为newInstance
或getInstance
),它返回封闭类的任何新的或现有的实例,并且可以返回子类或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中,条件,或为访客模式提供动力的双重间接。
希望这有帮助,只要赞同你已经拥有的答案!