这是Sum types的典型用例,也称为标记联合。不幸的是,Java不直接支持它们,因此必须使用某种变体实现访问者模式。
interface DocumentEvent {
}
interface MailEvent {
}
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(){
});
}
}
class MailEventImpl implements Event { ... }
public void divideEvent(Event event) {
event.accept(new EventDivider());
}
在这里,我尽可能地分离关注点,以便每个类和接口的责任都是唯一的。在现实生活中的项目中,DocumentEventImpl
, DocumentEvent
实现和 DocumentEvent
接口声明通常合并为单个类 DocumentEvent
,但这会引入循环依赖,并强制一些具体类之间的依赖(而我们知道,应该优先使用接口)。
此外,通常应该用类型参数替换 void
以表示结果类型,就像这样:
interface EventVisitor<R> {
R visitDocumentEvent(DocumentEvent event);
...
}
interface Event {
<R> R accept(EventVisitor<R> visitor);
}
这使得我们可以使用无状态访问者,这非常方便处理。
这种技术允许我们(几乎?)总是机械地消除instanceof
,而不必想出一个特定于问题的解决方案。