views:

59

answers:

3

Dependency injection is a useful technique but what approach is recommended when faced with runtime dependencies?

e.g. Say you want to glue an event to an event processor depending on the type of the event and the user who initiated the request.

public interface Event {}

public interface EventProcessor {
    public void handleEvent(Event e);
}

class EventProcessorFactory {
    private final User u;
    private final Event e;

    public EventProcessorFactory(User u, Event e) {
        this.u = u;
        this.e = e;
    }

    public EventProcessor get() {
        EventProcessor ep;
        if(e instanceof LocalEvent) {
            ep = new LocalEventProcessor();
        }
        else if(e instanceof RemoteTriggeredEvent && u instanceof AdminUser) { 
            //has static dependencies
            ep = new RemoteEventProcessor(u);
        }
        else {
            ep = new DefaultEventProcessor();
        }
    }
}

Now the complexity is encapsulated in the factory, but how else could I achieve the same result, without too much boilerplate code?

+2  A: 

You could use something like

public interface EventProcessor {
    public boolean supports(Event event, User user);
    public void handleEvent(Event event);
}

class EventProcessorFactory {
    public void setEventProcessors(List<EventProcessor> processors) {
        this.processors = processors;
    }
    public EventProcessor get(Event event, User user) {
        for (EventProcessor processor : processors) {
            if (processor.supports(event, user)
                return processor;
        }
    }
}

class LocalEventProcessor implements EventProcessor {
   public boolean supports(Event event, User user) {
        return (event instanceof LocalEvent);
   }
   // etc
}

class RemoteEventProcessor implements EventProcessor {
    public boolean supports(Event event, User user) {
        return (event instanceof RemoteTriggeredEvent) &&
               (user instanceof AdminUser);
    }
    // etc
}

If your processors have some sort of natural ordering, you can implement Comparable to ensure they are tested in the correct order, otherwise you'll can rely on them being injected into the factory in the required order, thus making it configurable.

ptomli
Thanks. It's a nice solution assuming the number of candidate processors to check is small.
parkr
A: 

You could make each Event responsible for creating the EventProcessor. That way you reduce the number of instanceof checks you're required to make and also achieve better encapsulation. Despite being more "OO" the trade-off is that the logic is no longer in one place (i.e. sometimes it's better to just stick with a "procedural approach" as per your example).

public interface Event {
  EventProcessor createProcessor(User u);
}

public class RemoteTriggeredEvent implements Event {
  public EventProcessor createProcessor(User u) {
    return (u instanceof AdminUser) ? new RemoteEventProcessor(u) :
      new DefaultEventProcessor();
  }
}
Adamski
+3  A: 

As written, what you call 'boilerplate code' looks to me to be just 'code'. You have some processing logic that needs to be stated somewhere (local events go to a local event processor, etc). Trying to avoid explicitly stating that logic could be a mistake.

If it is what you do want to do, the simplest way of doing it is to change the interface to add a method:

boolean isInterestedInEvent(Event e)

then set up all the event processors and loop over them until you find the right one.

soru
+1 - certainly looks like the *event processors* should expose the events they are interested in.
Nick Holt