使用Guava或其他库避免访问者模式?

3
在某些情况下,访问者模式会创建不必要的复杂性和样板文件(许多accept方法),而完全泛化似乎对我来说是不可能的(accept()方法返回值也是如此)。因此,在我的情况下,我认为需要另一种解决方案。
当然,我不想创建很多if-else instanceof块,我希望找到一种聪明的方式将特定类型(例如列表中的)与方法匹配。
例如,guava在其EventBus中具有类似的功能,可以将特定类型的事件与@Subscribe注释方法匹配,因此我正在寻找一种类似的库,以通用的方式实现这一点。
以下是一个例子,我想要类似的东西:
神奇接口:
/**  @param <R> - return type  * @param <I> - element super type */
public interface MagicDoubleDispatch<R, I>{
    R dispatch(I element);
}

我的“访客”实现(应该自动调用正确的方法)。
public class EventFormatter extends MagicDoubleDispatchImpl<String, Event> {
    private String format(AddedEmployeeEvent event) {
        return String.format("Added new employee: %s with id %s", event.name, event.employeeId);
    }
    private String format(DeletedEmployeeEvent event) {
        return String.format("Deleted employee:(%s) %s (this can't be undone!)", event.employeeId, event.name);
    }
    private String format(AddedTimeCardEvent event) {
        return String.format("Timecard of %s has been added to %s", event.date, event.employeeName);
    }
}

我希望这是一个库(例如):
public abstract class MagicDoubleDispatchImpl<R, I> implements MagicDoubleDispatch<R, I> {
    //I want this ... probably using reflection as EventBus, I don't care..
}

客户端代码:
private EventFormatter eventFormatter = new EventFormatter();

public List<String> toStringList(List<Event> events) {
    return events.stream()
        .map(it -> eventFormatter.dispatch(it))
        .collect(Collectors.toList());
}

为什么不使用Guava? - aglassman
我之所以写了这个是出于同样的原因。它旨在用作一种碰撞系统,但我认为它也可以以更通用的方式使用。 - ChiefTwoPencils
Aglassaman:是的,如果可能的话我想使用guava,但不要用eventbus,因为那只是针对特定功能的,我想要一种更通用的双重分派方式。 - Daniel Hári
ChiefTwoPencils:我很好奇你的库,有没有什么例子可以参考? - Daniel Hári
https://github.com/ChiefTwoPencils/colli-da-scope/tree/master/examples - user180100
2个回答

1

不确定您所说的"accept()方法返回值也是"是什么意思,但无论如何,访问者模式可以通过代码生成器变得非常轻量化:请查看https://github.com/derive4j/derive4j - 它提供了一种模式匹配语法,可以使您的代码更易读。

此外,没有其他技术/库能够像访问者模式一样提供如此多的类型安全性。


我指的是 "accept() 方法返回值也一样":public <B> B accept(Visitor<...> v).... - Daniel Hári
这很聪明,但这是代码生成吗?我想要一种不需要生成 Java 代码的解决方案。 - Daniel Hári
你对避免编译时(JSR269)代码生成的理由感到好奇吗?对我来说,任何基于运行时反射的解决方案都明显不如编译时生成的解决方案(不具备类型安全性)。 - JbGi
也许我不知道你所说的编译时生成是什么意思,你能给我举个例子吗? - Daniel Hári
1
JPA2的Criteria API利用JSR269注解处理器在编译时生成实体的静态元模型(代码生成由javac驱动)。这使得可以创建类型安全的查询。 - JbGi

0
你可以创建一个Map,将Event作为key并配合一个将事件转换为字符串的function。
Map<Class<T extends Event>,Function<T,String>>

如果你想要一些花哨的东西,你可以使用注解并创建一个基于这些注解的注册表(在Spring中很容易实现),但基本上最终结果是相同的。

EventFormatter根据事件类型在映射中查找给定事件的格式化程序,并应用相应的函数。如果事件类和格式化程序之间存在1对1的映射,那么应该没问题。

如果需要在给定类型没有格式化程序时回退到父类,那就有点麻烦了,但还是可行的。


作为一般解决方案,返回值可以是任何类型,不仅仅是字符串,字符串只是示例。 - Daniel Hári
您可以将String替换为另一个通用类型,例如Map<Class<T extends Event>,Function<T,U>>。 - nhu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接