0

I'm facing a problem with my current design for an Event/Action mechanism. There are numerous Events, that can be distinguished by certain criteria. This results in a class hierarchy.

public interface Event , public abstract class EventCategory implements Event and an actual event e.g. public class ActualEvent extends EventCategory . Then there are Actions which are able to perform operations based on the Event given.

public interface Action<T extends Event> {
  void execute(T event);
}

and an actual Action ActualAction<T extends EventCategory> implements Action<T> . Now there are Factories that generate the specific instances,

public interface ActionFactory<T extends Event> {
  Action<T> create(String actionData);
}

public class ActualActionFactory implements ActionFactory<EventCategory> {
  @Override 
  Action<T> create(String actionData);
}

Finally there are EventConsumers, each has one action and will handle the occurence of events and will execute the constructed action.

public interface EventConsumer<T extends Event> {
  void handleEvent(T event);
}

public interface EventConsumerFactory<T extends Event> {
  EventConsumer<T> create(String data, Action<T> action);
}

The orchestrator class, which holds all ActionFactories and delegates the create Calls

public class Orchestrator {
 private final List<ActionFactory<? extends Event>> factories = Lists.newArrayList();
 private final List<EventConsumerFactories<? extends Event>> consumerFactories = Lists.newArrayList();
 
 public void createAction(String actionData) {
   for (ActionFactory<? extends Event> factory : factories) {
     Action<? extends Event> action = factory.create(actionData);
     if (action != null) {
       consumerFactories.forEach(cf -> cf.create(null, action));
     }
 }

Im struggling with the last part. If type the Action as above, Action<? extends Event> the create call for consumerFactories complains "Required <capture of ? extends Event>, provided <capture of ? extends Event>".

What is the problem with my class hierarchy or template bounds? If use raw types for the action at this place everything works and the actions execute for the configured events. But i would like to get rid of the raw types.

Thanks for your help!

Kind Regards, Andreas

Edit: adding code how factories are registered

public interface EventConsumerFactory<T extends Event> {
  EventConsumer<T> create(String consumerData, Action<? super Event> action);
}
public class ActualEventConsumerFactory implements EventConsumerFactory<ActualEvent>  {
}

Orchestrator Registration

  void registerConsumerFactory(ConsumerFactory<? super Event> factory) {
    this.consumerFactories.add(factory);
   }
Draess
  • 218
  • 1
  • 11
  • A *consumer* shouldn't have an `extends` wildcard: see [What is PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super/53394456#53394456). – Andy Turner Aug 07 '20 at 11:08
  • (Aside: what's the difference between an `Action` and an `EventConsumer`?) – Andy Turner Aug 07 '20 at 11:09
  • thanks for your quick reply. An EventConsumer is a configured chain between one Event and one Action. E.g. An EventConsumer might hold further conditions that need to be fulfilled in order to execute the provided Action. The possible Events and Actions are programmatically defined, but their Connections can be configured from outside, e.g. a database. – Draess Aug 07 '20 at 11:15

1 Answers1

1

The fundamental issue here is that you're using the wrong bound type.

Both EventConsumer and Action accept instances of T: they're consumers. As such, you don't want to be declaring them with extends bounds, because you won't be able to pass anything to them (other than literal null).

You code compiles if you change these bounds to super:

interface EventConsumerFactory<T extends Event> {
  EventConsumer<T> create(String data, Action<? super T> action);
                                             // here
}

class Orchestrator {
  private final List<ActionFactory<? super Event>> factories = Lists.newArrayList();
                                  // here
  private final List<EventConsumerFactory<? super Event>> consumerFactories = Lists.newArrayList();
                                         // here

  public void createAction(String actionData) {
    for (ActionFactory<? super Event> factory : factories) {
                      // here
      Action<? super Event> action = factory.create(actionData);
            // here
      if (action != null) {
        consumerFactories.forEach(cf -> cf.create(null, action));
      }
    }
  }
}
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Thanks for your hint with PECS, I fixed the relevant places. Last remaining problem now is to register the factories in the `Orchestrator` class. `registerFactory(EventConsumerFactory super Event> factory)`, but with actual class `ActualEventConsumerFactory implements ConsumerFactory` says Required ConsumerFactory super Event> provided ConsumerFactory. – Draess Aug 07 '20 at 12:19
  • @user1801173 that’s some code you didn’t show. Can you edit your question to include the `registerFactory` method and other relevant code? – Sweeper Aug 07 '20 at 12:23
  • I edited my post showing ConsumerFactory and factory registration – Draess Aug 07 '20 at 12:35