我有一组所有需要以某种(个人)方式行事的课程。
通常我只使用单个DoSomethingInterface
方法创建doSomething()
,并让每个类以适合每个类需要的方式实现该方法。但是,在这种情况下我不能这样做,因为类定义是不可修改的(自动生成的)
所以,我认为我需要创建一组不同的类,每个类都使用一个自动生成的类并对它们执行操作。所以,假设我有2个自动生成的类Class1
和Class2
,我将首先定义一个公共Operator
接口:
public interface Operator <TYPE>{
public void doSomething(TYPE type);
}
然后按类
实现其中一个public class Class1Operator implements Operator<Class1>{
public void doSomething(Class1 type){
...
...
}
}
和
public class Class2Operator implements Operator<Class2>{
public void doSomething(Class2 type){
...
...
}
}
好的,到目前为止一切顺利。现在,鉴于我有一个Class1
类型的对象,有没有办法让它的运算符不依赖:
public Operator getOperator(Object obj){
if(obj instanceof Class1){
return new Class1Operator();
}else if(obj instanceof Class2{
return new Class2Operator();
}
return null;
}
对我来说哪种做法似乎不好......
我能想到的另一种方法是通过创建类名的运算符映射,如下所示:
Map<Class, Operator> allOperators = new HashMap<Class, Operator>();
allOperators.put(Class1.class, new Class1Operator());
allOperators.put(Class2.class, new Class2Operator());
然后使用以下命令返回运算符:
public Operator getOperator(Object obj){
return allOperators.get(obj);
}
但这似乎并不正确(我不确定,在同类课程中键入一个对象是否有任何问题......)
关于这些方法中的任何一种是否“正确”的任何输入?还是有更优雅的解决方案??
由于
答案 0 :(得分:4)
您实施的内容(逐类方法)是我教授模式时谈论的GoF访客模式的替代方案之一。即使在运行时,它也是高效且可扩展的。比if / else if / else硬连线方法好多了。
键入类的唯一问题是实际实例是否实现了子类型而不是您提到的类类型;那么查找地图将不起作用。
如果你需要识别子类型,我会推荐Aaron的方法(走上超类链),但你也可能想看看已实现的接口。如果您只需要“完全匹配”,请保持getOperator简单。
请注意,您在getOperator中有一个错误 - 它应如下所示:
public Operator getOperator(Object obj){
return allOperators.get(obj.getClass());
}
还有一件事......将您的地图隐藏在另一个类中并按如下方式进行管理:
private Map<Class<?>, Operator<?>> map = new HashMap<Class<?>, Operator<?>>();
public <T> void register(Class<T> clazz, Operator<T> operator) {
map.put(clazz, operator);
}
这可以防止任何人注册不会对其键入的类进行操作的运算符。 (您可能希望使用Operator作为参数来允许针对超类编写的运算符,但可能不需要)
答案 1 :(得分:2)
构建映射的一个问题是它不支持子类,除非你专门注册它们或者扩展你的get函数来专门查找超类。
也就是说,如果B继承自A并且您已经使用A.class注册了运算符。使用B.class获取运算符将失败,除非您将getOperator更改为:
public Operator getOperator(Object obj){
Class<?> current = obj.getClass();
Operator op;
while((op = allOperators.get(current)) == null){
current = current.getSuperclass();
if(current == null){
/*
* We've walked all the way up the inheritance hierarcy
* and haven't found a handler.
*/
return null;
}
}
return op;
}
一旦你有了一个合理的getOperator实现,将类映射到运算符似乎是一种合理的方法。
答案 2 :(得分:1)
您可以通过Class.isAssignableFrom来解决子类型问题。我一直都在使用它,虽然它不是“访客”的优雅,但在实践中却非常好。
答案 3 :(得分:0)
是否可以创建自己的类来扩展生成的类,然后让类实现接口?
答案 4 :(得分:0)
我要说基于Java处理泛型的方式,不可能只使用接口本身。
在Java中,泛型在编译时被删除,并被替换为强制转换。
我实际上没有检查过它在内部是如何工作的,但是猜测一下,你的界面变成了这个:
public interface Operator {
public void doSomething(Object type);
}
以及它被调用的地方:
public class Class1Operator implements Operator{
public void doSomething(Object type){
Class1 oType = (Class1) type;
...
...
}
}
这仍然不完全正确,因为类型将在返回之后被转换,加上Java字节码实际上看起来不像Java,但是你可能会得到一般的想法。
instanceof和Map方法应该可以工作,即使它们有点乱。
答案 5 :(得分:0)
你有没有考虑过这个:
public Interface Operator {
public void doSomething();
}
public class Class1Operator extends Class1 implements Operator {
...
}
public class Class2Operator extends Class2 implements Operator {
...
}
但是参考你的第二个问题,即在没有真正需要做“instanceof”mojo的情况下将操作符移到一个对象上(我猜这看起来不干净):
我建议如果你不能根据你的确切需要修改你的类,请写一个包装器:
public Interface Operator<T> {
public void doSomething(T obj);
}
public Interface WrappedObject<T> {
public Operator<T> getOperator();
}
public class WrappedClass1 extends Class1 implements WrappedObject<Class1> {
...
}
public class WrappedClass2 extends Class2 implements WrappedObject<Class2> {
...
}
public class Class1Operator implements Operator<Class1> {
...
}
public class Class2Operator implements Operator<Class2> {
...
}
这会满足您的需求吗?
对于完全不符合您需求的类来编写包装器总是一个很好的做法,并且不能由您控制。它可以帮助您保持代码健康,即使这些野生类发生了变化。
干杯,
JRH。