【问题标题】:Dynamic dispatch without instanceof or getClass() in JavaJava 中没有 instanceof 或 getClass() 的动态调度
【发布时间】:2016-04-06 16:01:00
【问题描述】:

我有以下域对象:

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 的现有问题,但他们都建议将一些逻辑直接添加到域对象中。但是,在这种情况下,我不想用特定于我的模块的逻辑来污染它们。

【问题讨论】:

  • 为什么不能从Event实现FirstEventSecondEvent然后调用方法?
  • 我认为没有第三种选择。 99% 的情况下,我会同意代码审查员的观点,即您应该使用继承而不是 instanceofgetClass,但也有例外。
  • @DeendayalGarg 不确定我是否理解您的问题。 FirstEventSecondEvent 已经在另一个模块中实现。
  • @PaulBoddington 这对我来说也是一个正确的答案,至少当他们开始抱怨时我会在代码审查期间得到一些支持
  • 访问者模式呢?

标签: java design-patterns instanceof dynamic-dispatch


【解决方案1】:

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 接口必须枚举它们。您可以通过一种包罗万象的方法“有点”解决这个问题,但它很混乱,而且仍然难以升级。

【讨论】:

    【解决方案2】:

    排除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

    【讨论】:

      猜你喜欢
      • 2015-09-04
      • 2011-06-26
      • 1970-01-01
      • 2012-01-06
      • 1970-01-01
      • 2021-07-12
      • 2012-12-13
      • 2011-06-09
      相关资源
      最近更新 更多