Search code examples
javaoopfactoryvisitor-patternabstract-factory

Return data from void-visitor without changing the abstract visitor


I am using a Java framework that provides some kind of visitor pattern for processing elements. There is an abstract super class AbstractProcessor<T> (which I cannot change) that provides a method public abstract void process(T visitedElement).

I then have defined several concrete implementations of such processors and instantiate them by Factory Pattern to then use them via their common supertype. Now, how do I get any type of information out of there? I obviously cannot simply return something, since the abstract process-method is of type void.

My only thought so far would be something like a data-field I can add to the concrete implementation (since I cannot change the abstract super class), so to do smth like this:

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

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

But to receive this data from - lets say a list of their common super types that do not posess the data-field - I would have to do some ugly type checking and casting like this:

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

Is there any other way to retrieve some type of data that gets calculated during the process method this way?


Solution

  • I think you have an intrinsically unsolvable problem here. I understand you're saying:

    • You've written several AbstractProcessor subclasses.
    • They all do different things and produce different results.
    • You want to treat them all homogeneously, as instances of AbstractProcessor.

    But you can't treat them all as instances of the same class, AbstractProcessor, if they all produce different results. You have to know what type of processor you have in order to interpret the results.

    You have two options. The first option is to unify the results. For example, you could have an interface called ResultHandler like this:

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

    Pass in an instance of ResultHandler (either during construction or in a separate handleResult method), then (within each processor) invoke the ResultHandler method appropriate to that processor type. If you have several different processors that generate sums in some way, at least they can all call the same handleSumResult API and you don't have to do instanceof anywhere.

    The other strategy is to just abandon your attempt to treat all processors homogeneously. I think this might be the better option. Your code obviously knows what processor it needs to use, so just go ahead and instantiate that one, use it, and collect the results from whatever API you define. If there's some reason why you have to treat the processors homogeneously (e.g., the processor class is specified by the user, maybe in a configuration file), then move that abstraction up one level and instead have the user specify a class that owns the entire process of processing the data and collecting the results. Instead of specifying MyProcessor, the use specifies MySomethingElse, and then MySomethingElse both instantiates MyProcessor and handles the results.