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