避免Java中的“instanceof”

我有以下(也许是常见的)问题,目前绝对困惑我:

有几个生成的事件对象扩展了抽象类Event ,我想把它们分成Session Beans,就像

 public void divideEvent(Event event) { if (event instanceof DocumentEvent) { documentGenerator.gerenateDocument(event); } else if (event instanceof MailEvent) { deliveryManager.deliverMail(event); ... } ... } 

但是将来可能会有两种以上的事件types,所以if-else会很长,也许不可读。 另外我认为instanceof在这种情况下并不是真正的“最佳实践”。

我可以将一个抽象方法添加到Eventtypes,并让它们自己分割,但是我必须在每个实体中注入特定的Session Beans。

是否有任何暗示为这个问题实现“漂亮”的解决scheme?

谢谢你的帮助!

最简单的方法是让事件提供一个可以调用的方法,以便事件知道该怎么做。

 interface Event { public void onEvent(Context context); } class DocumentEvent implements Event { public void onEvent(Context context) { context.getDocumentGenerator().gerenateDocument(this); } } class MailEvent implements Event { public void onEvent(Context context) { context.getDeliveryManager().deliverMail(event); } } class Context { public void divideEvent(Event event) { event.onEvent(this); } } 

多态是你的朋友。

 class DocumentGenerator { public void generate(DocumentEvent ev){} public void generate(MainEvent ev){} //... and so on } 

然后就是

  DocumentGenerator dg = new DocumentGenerator(); // .... dg.generate(event); 

更新

许多人提出反对意见,认为“编译时必须知道事件的种类”。 而且,是的,你显然必须知道在发生器部分的编译时你正在解释什么事件,什么时候可以写出生成部分?

这些竞争的例子使用命令模式,这很好,但是意味着事件必须知道细节,不仅仅是它们的表示,而且也知道如何打印它们的表示。 这意味着每个class级都可能有两种需求变更:敏感的变化:事件所代表的变化以及事件在印刷中的performance方式的变化。

现在考虑,例如,需要国际化这个。 在命令模式的情况下,你必须去n个类的n个不同的事件types,并写新的做的方法。 在多态的情况下,这些更改被本地化为一个类。

当然,如果你需要国际化一次,你可能需要很多种语言,这就促使你在命令模式的案例中为每个类添加类似于战略的东西,现在需要n个类× m个语言; 再次,你只需要在多态的情况下有一个策略和一个类。

有理由select这两种方法之一,但声称多态方法是错误的只是不正确的。

每个事件都有一个function,比如说。 每个子类都会覆盖,做(:P)适当的动作。 dynamic调度之后做一切事情。 所有你需要做的就是调用event.do()

我没有评论权利,我不知道确切的答案。 但是,这只是我或一些脂肪酶在这里build议使用重载(这发生在编译时间,因此只是产生编译错误)来解决这个问题?

只是一个例子。 如你所见,它不会被编译。

 package com.stackoverflow; public class Test { static abstract class Event {} static class MailEvent extends Event {} static class DocEvent extends Event {} static class Dispatcher { void dispatchEvent(DocEvent e) { System.out.println("A"); } void dispatchEvent(MailEvent e) { System.out.println("B"); } } public static void main(String[] args) { Dispatcher d = new Dispatcher(); Event e = new DocEvent(); d.dispatchEvent(e); } 

利用方法parsing顺序有什么问题?

 public void dispatchEvent(DocumentEvent e) { documentGenerator.gerenateDocument(event); } public void dispatchEvent(MailEvent e) { deliveryManager.deliverMail(event); } 

让Java执行匹配正确参数types的工作,然后正确地分派事件。

这是Sumtypes的一个典型用例,也被称为带标签的联合。 不幸的是,Java不直接支持它们,所以它们必须使用访问者模式的一些变体来实现。

 interface DocumentEvent { // stuff specific to document event } interface MailEvent { // stuff specific to mail event } interface EventVisitor { void visitDocumentEvent(DocumentEvent event); void visitMailEvent(MailEvent event); } class EventDivider implements EventVisitor { @Override void visitDocumentEvent(DocumentEvent event) { documentGenerator.gerenateDocument(event); } @Override void visitMailEvent(MailEvent event) { deliveryManager.deliverMail(event); } } 

这里我们定义了我们的EventDivider ,现在提供一个调度机制:

 interface Event { void accept(EventVisitor visitor); } class DocumentEventImpl implements Event { @Override void accept(EventVisitor visitor) { visitor.visitDocumentEvent(new DocumentEvent(){ // concrete document event stuff }); } } class MailEventImpl implements Event { ... } public void divideEvent(Event event) { event.accept(new EventDivider()); } 

在这里,我使用了最大可能的关注点分离,以便每个类和接口的责任是唯一的。 在现实生活中,项目DocumentEventImplDocumentEvent实现和DocumentEvent接口声明通常合并成一个DocumentEvent类,但是引入了循环依赖关系,并强制了具体类之间的一些依赖关系(我们知道,应该更喜欢依赖于接口)。

另外,通常应该用一个types参数replacevoid来表示结果types,如下所示:

 interface EventVisitor<R> { R visitDocumentEvent(DocumentEvent event); ... } interface Event { <R> R accept(EventVisitor<R> visitor); } 

这允许使用无状态的访问者,这是非常好的处理。

这种技术允许(几乎?)总是消除机械的instanceof而不是必须找出问题的具体解决scheme。

您可以针对每种事件types注册每个处理程序类,并在事件发生时执行调度。

 class EventRegister { private Map<Event, List<EventListener>> listerMap; public void addListener(Event event, EventListener listener) { // ... add it to the map (that is, for that event, get the list and add this listener to it } public void dispatch(Event event) { List<EventListener> listeners = map.get(event); if (listeners == null || listeners.size() == 0) return; for (EventListener l : listeners) { l.onEvent(event); // better to put in a try-catch } } } interface EventListener { void onEvent(Event e); } 

然后让你的具体处理程序来实现接口,并注册这些处理程序与事件注册。

你可以有一个Dispatcher接口,定义如

 interface Dispatcher { void doDispatch(Event e); } 

DocEventDispatcherMailEventDispatcher等实现

然后定义一个Map<Class<? extends Event>, Dispatcher> Map<Class<? extends Event>, Dispatcher> ,其条目类似于(DocEvent, new DocEventDispatcher()) 。 那么你的派遣方法可以减less到:

 public void divideEvent(Event event) { dispatcherMap.get(event.getClass()).doDispatch(event); } 

这是一个unit testing:

 public class EventDispatcher { interface Dispatcher<T extends Event> { void doDispatch(T e); } static class DocEventDispatcher implements Dispatcher<DocEvent> { @Override public void doDispatch(DocEvent e) { } } static class MailEventDispatcher implements Dispatcher<MailEvent> { @Override public void doDispatch(MailEvent e) { } } interface Event { } static class DocEvent implements Event { } static class MailEvent implements Event { } @Test public void testDispatcherMap() { Map<Class<? extends Event>, Dispatcher<? extends Event>> map = new HashMap<Class<? extends Event>, Dispatcher<? extends Event>>(); map.put(DocEvent.class, new DocEventDispatcher()); map.put(MailEvent.class, new MailEventDispatcher()); assertNotNull(map.get(new MailEvent().getClass())); } }