避免在 Java 中使用“instanceof”

2022-08-31 15:25:03

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

有几个生成的事件对象扩展了抽象类,我想将它们划分为会话Bean,例如Event

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

}

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

我可以向类型中添加一个抽象方法,并让它们自行划分,但随后我必须在每个实体中注入特定的会话Bean。Event

是否有任何提示可以解决此问题的“漂亮”解决方案?

感谢您的任何帮助!


答案 1

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

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

答案 2

多态性是你的朋友。

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

然后只是

 DocumentGenerator dg = new DocumentGenerator();

 // ....
 dg.generate(event);

更新

许多人提出了反对意见,即您“必须在编译时知道事件的种类”。而且,是的,您显然必须知道在生成器部分的编译时正在解释哪些事件,否则您将何时能够编写生成部分?

这些相互竞争的示例使用命令模式,这很好,但意味着事件不仅要知道其表示的详细信息,还要知道如何打印其表示。这意味着每个类可能有两种敏感的需求变化:事件表示的内容的变化,以及事件在印刷品中的表示方式的变化。

现在,考虑一下,例如,需要将其国际化。在命令模式的情况下,您必须为n个不同的事件类型转到n个类并编写新的do方法。在多态性情况下,更改将本地化为一个类。

当然,如果你需要国际化一次,你可能需要多种语言,这驱使你在命令模式情况下为每个类添加类似策略的东西,现在需要n个类×m种语言;同样,在多态性情况下,您只需要有一个策略和一个类。

选择这两种方法都是有原因的,但声称多态性方法是错误的是不正确的。