Java 中没有 instanceof 或 getClass() 的动态调度
Dynamic dispatch without instanceof or getClass() in Java
我有以下域对象:
public interface Event {}
public class FirstEvent {}
public class SecondEvent {}
然后我有另一个模块,它应该与我的域对象完全分离,这意味着它知道域对象,但域对象不应该知道这个附加模块的存在。
在这个模块中,我通过公共接口接收对象 Event
,我需要根据特定的事件类型采取不同的行动。
目前我的代码如下所示:
if (event instanceof FirstEvent.class) {
doFirst();
}
else if (event instanceof SecondEvent.class) {
doSecond();
}
它工作正常,但静态分析工具和代码审阅者抱怨我不应该使用 instanceof
,我应该用更面向对象的方法代替它。反射或 getClass()
也不是一个选项。
如何在 Java 中做到这一点?
我已经审阅了许多关于替换 instanceof
的现有问题,但所有这些问题都建议直接将一些逻辑添加到域对象中。但是,在这种情况下,我不想用我的模块特有的逻辑来污染它们。
Visitor 模式,又名 Double Dispatch,通常在这里很有用。
为每个已知的事件类型定义了一个接口,每个事件都实现了一个接口方法,允许外部对象使用该接口的实现来调用它。然后该事件确保接口的类型特定方法使用其自己的 'this' 引用进行调用,因此您不会得到任何显式向下转换。
public interface EventVisitor {
visit(FirstEvent firstEvent);
visit(SecondEvent secondEvent);
}
public class FirstEvent {
...
public void allowVisit(EventVisitor ev) {
ev.visit(this); // calls the 'FirstEvent' overriden method
}
...
}
public class SecondEvent {
...
public void allowVisit(EventVisitor ev) {
ev.visit(this); // calls the 'SecondEvent' overriden method
}
...
}
public class MyOtherObject implements EventVisitor, EventListener {
...
public void signalEvent(Event e) {
e.allowVisit(this);
}
public void visit(FirstEvent e) {
// handle FirstEvent type
}
public void visit(SecondEvent e) {
// handle SecondEvent type
}
}
这种类型的缺点是很难添加新的事件类型,因为您的 EventListener 接口必须枚举它们。您可以 'kind' 使用万能的方法来解决这个问题,但它很混乱并且仍然难以升级。
排除 Reflection
是可以理解的,但使用 getClass()
应该不是问题。
我解决这个问题的方法:
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class DynamicDispatch{
Map<String,Event> events = new ConcurrentHashMap<String,Event>();
public DynamicDispatch(){
Event event = new FirstEvent();
events.put(event.getName(),event);
event = new SecondEvent();
events.put(event.getName(),event);
}
public Event getEvent(String eventName){
return events.get(eventName);
}
public static void main(String args[]){
DynamicDispatch dispatchObj = new DynamicDispatch();
Event event = dispatchObj.getEvent(args[0]);
System.out.println("dispatchObj:"+event+":"+event.getName());
}
}
interface Event {
public String getName();
}
class FirstEvent implements Event{
public String getName(){
//return this.getClass().getSimpleName();
return "FirstEvent";
}
}
class SecondEvent implements Event{
public String getName(){
//return this.getClass().getSimpleName();
return "SecondEvent";
}
}
输出:
java DynamicDispatch FirstEvent
dispatchObj:FirstEvent@72d86c58:FirstEvent
java DynamicDispatch SecondEvent
dispatchObj:SecondEvent@72d86c58:SecondEvent
我仍然更喜欢使用 return this.getClass().getSimpleName();
而不是对值进行硬编码。
我希望上面的代码可以与 static analysis tools
一起使用。
我有以下域对象:
public interface Event {}
public class FirstEvent {}
public class SecondEvent {}
然后我有另一个模块,它应该与我的域对象完全分离,这意味着它知道域对象,但域对象不应该知道这个附加模块的存在。
在这个模块中,我通过公共接口接收对象 Event
,我需要根据特定的事件类型采取不同的行动。
目前我的代码如下所示:
if (event instanceof FirstEvent.class) {
doFirst();
}
else if (event instanceof SecondEvent.class) {
doSecond();
}
它工作正常,但静态分析工具和代码审阅者抱怨我不应该使用 instanceof
,我应该用更面向对象的方法代替它。反射或 getClass()
也不是一个选项。
如何在 Java 中做到这一点?
我已经审阅了许多关于替换 instanceof
的现有问题,但所有这些问题都建议直接将一些逻辑添加到域对象中。但是,在这种情况下,我不想用我的模块特有的逻辑来污染它们。
Visitor 模式,又名 Double Dispatch,通常在这里很有用。
为每个已知的事件类型定义了一个接口,每个事件都实现了一个接口方法,允许外部对象使用该接口的实现来调用它。然后该事件确保接口的类型特定方法使用其自己的 'this' 引用进行调用,因此您不会得到任何显式向下转换。
public interface EventVisitor {
visit(FirstEvent firstEvent);
visit(SecondEvent secondEvent);
}
public class FirstEvent {
...
public void allowVisit(EventVisitor ev) {
ev.visit(this); // calls the 'FirstEvent' overriden method
}
...
}
public class SecondEvent {
...
public void allowVisit(EventVisitor ev) {
ev.visit(this); // calls the 'SecondEvent' overriden method
}
...
}
public class MyOtherObject implements EventVisitor, EventListener {
...
public void signalEvent(Event e) {
e.allowVisit(this);
}
public void visit(FirstEvent e) {
// handle FirstEvent type
}
public void visit(SecondEvent e) {
// handle SecondEvent type
}
}
这种类型的缺点是很难添加新的事件类型,因为您的 EventListener 接口必须枚举它们。您可以 'kind' 使用万能的方法来解决这个问题,但它很混乱并且仍然难以升级。
排除 Reflection
是可以理解的,但使用 getClass()
应该不是问题。
我解决这个问题的方法:
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class DynamicDispatch{
Map<String,Event> events = new ConcurrentHashMap<String,Event>();
public DynamicDispatch(){
Event event = new FirstEvent();
events.put(event.getName(),event);
event = new SecondEvent();
events.put(event.getName(),event);
}
public Event getEvent(String eventName){
return events.get(eventName);
}
public static void main(String args[]){
DynamicDispatch dispatchObj = new DynamicDispatch();
Event event = dispatchObj.getEvent(args[0]);
System.out.println("dispatchObj:"+event+":"+event.getName());
}
}
interface Event {
public String getName();
}
class FirstEvent implements Event{
public String getName(){
//return this.getClass().getSimpleName();
return "FirstEvent";
}
}
class SecondEvent implements Event{
public String getName(){
//return this.getClass().getSimpleName();
return "SecondEvent";
}
}
输出:
java DynamicDispatch FirstEvent
dispatchObj:FirstEvent@72d86c58:FirstEvent
java DynamicDispatch SecondEvent
dispatchObj:SecondEvent@72d86c58:SecondEvent
我仍然更喜欢使用 return this.getClass().getSimpleName();
而不是对值进行硬编码。
我希望上面的代码可以与 static analysis tools
一起使用。