Return 来自 void-visitor 的数据,不改变抽象访问者

Return data from void-visitor without changing the abstract visitor

我正在使用 Java 框架,该框架为处理元素提供了某种访问者模式。有一个抽象的 super class AbstractProcessor<T> (我不能改变)它提供了一个方法 public abstract void process(T visitedElement).

然后我定义了这些处理器的几个具体实现,并通过工厂模式实例化它们,然后通过它们的通用超类型使用它们。现在,我如何从那里获取任何类型的信息?我显然不能简单地 return 一些东西,因为抽象过程方法是 void 类型。

到目前为止我唯一的想法是我可以添加到具体实现中的数据字段之类的东西(因为我无法更改抽象超级 class),所以这样做:

public class MyProcessor extends AbstractProcessor<SomeType> {
public String data;

  @Override
  public void process(SomeType t) {
  [do stuff...]
  this.data = "some important info";
  }
}

但是要从中接收这些数据 - 让我们说一个不拥有数据字段的常见超类型的列表 - 我将不得不像这样进行一些丑陋的类型检查和转换:

List<AbstractProcessor> list = getProcessors();
list.forEach(p -> {
  someType.processWith(p); //someType accepts AbstractProcessor's and then runs their process-method
  if(p instanceOf MyProcessor) 
    System.out.println( ((MyProcessor)p).data );
  }
});

是否有任何其他方法可以检索以这种方式在 process 方法期间计算的某种类型的数据?

我不会在这里使用字段。相反:定义一个合适的接口,比如 ResultProcessor 有一个 T getResult() 方法。然后让所有需要产生结果的子类实现该接口。

当然,您仍然需要转换,但至少您可以使用泛型 T 来保持灵活性,并且您的客户可以调用方法而不是直接访问字段。

我认为你这里有一个本质上无法解决的问题。我明白你在说:

  • 您已经写了几个 AbstractProcessor 子class。
  • 他们做不同的事情并产生不同的结果。
  • 您想将它们全部视为 AbstractProcessor 的实例。

但是如果它们都产生不同的结果,您不能将它们都视为相同 class、AbstractProcessor 的实例。你必须知道你有什么类型的处理器才能解释结果。

你有两个选择。第一个选项是统一结果。例如,您可以有一个名为 ResultHandler 的接口,如下所示:

interface ResultHandler {
   void handleSumResult(int result);
   void handleConcatResult(String result);
   void handleSomeOtherProcessorResult(Whatever result);
}

传入 ResultHandler 的实例(在构造期间或在单独的 handleResult 方法中),然后(在每个处理器中)调用适合该处理器类型的 ResultHandler 方法.如果您有几个不同的处理器以某种方式生成总和,至少它们都可以调用相同的 handleSumResult API 而您不必在任何地方执行 instanceof

另一种策略是放弃对所有处理器一视同仁的尝试。我认为这可能是更好的选择。您的代码显然知道它需要使用什么处理器,所以只需继续实例化那个处理器,使用它,然后从您定义的 API 中收集结果。如果出于某种原因必须对处理器进行同等处理(例如,处理器 class 由用户指定,可能在配置文件中指定),则将该抽象向上移动一个级别,而是让用户指定一个 class 拥有处理数据和收集结果的整个过程。使用指定 MySomethingElse 而不是指定 MyProcessor,,然后 MySomethingElse 都实例化 MyProcessor 并处理结果。

例如,您可以使处理器 "push" 数据成为散列映射,该散列映射是传入的或可全局访问的。

public class MyProcessor extends AbstractProcessor<SomeType> {
  public Map<Object, String> data;

  @Override
  public void process(SomeType t) {
  [do stuff...]
  this.data.put(this, "some important info");
  }
}

这样客户端就可以知道哪个抽象处理器添加了哪些信息,它所需要的只是访问映射。