9

I have an enum of TriggerType, where different triggers can be added

public enum TriggerType {
    meta,data,list,toggle
}

These trigger types are used inside different handlers (eg Component, Dashboard etc) to identify which trigger is triggered inside the handler through a switch-case, for eg Code snippet of ComponentHandler using trigger through switch-case is given below

@Override
public TriggerResultInterface executeTriggerJob(TriggerEventHelper triggerEventHelper) throws TriggerHandlerException {
    switch (triggerEventHelper.getTriggerName()) {
        case meta:
            return getMetaComponentConfig(triggerEventHelper);
        case data:
            return getComponentData(triggerEventHelper);
        default:
            LOGGER.debug(INVALID_TRIGGER_NAME_CONFIGURED);
            throw new TriggerHandlerException(INVALID_TRIGGER_NAME_CONFIGURED);
    }

}

Imagine If I want to add a new Trigger, I have to update the enum class which is unavoidable, at the same time I have to update each of my handler classes which that Trigger need to be used, Is this way of design with coding is good or is there any-other better solution that will enhance this code and follows SOLID principles along with a better design.

I would like to stress on saying that this question is not a duplicate of this. There is only one behavior needed in that situation for each type (eg: convertToMp3). But what my question refers to is my enum type (Trigger Type) is dependent on Handlers which it could possibly be used, so each Trigger Type enum's behavior or implementation will depend on the requirement of the handler it is being used.

Community
  • 1
  • 1
Keshan Nageswaran
  • 8,060
  • 3
  • 28
  • 45
  • The command pattern might fit your use case, but as I have seen it implemented, there is still a `switch` statement to figure out which command to invoke. – Tim Biegeleisen Aug 20 '16 at 06:52
  • 2
    Put the method(s) inside the Enum, with overrides for each value. I find in practice there is limited value in doing this, as you have to provide various kinds of external context to each method which kind of destroys the Enum's ability to stand alone, but if you can limit that damage it can be a useful technique. – user207421 Aug 20 '16 at 06:53
  • 2
    The [answer I wrote](http://stackoverflow.com/a/39045135/2398375) for you about the context handlers earlier today, that's how you'd do it – Vince Aug 20 '16 at 07:22
  • @Vince Emigh That is different scenario from this, here there is different implementation for each trigger type inside each handler so for each enum type(i.e. Trigger Type) will have different definitions depending on its usage in the handlers, and these triggers need to be classes possibly – Keshan Nageswaran Aug 20 '16 at 07:39
  • @Raedwald Nope this is not a duplicate of that question, it has only one behavior for each type for eg (convertToMp3) but here each Trigger Type behavior depends on its handlers requirement, so Trigger Type behavior changes (implementation changes) inside each handler where it is used – Keshan Nageswaran Aug 20 '16 at 07:42
  • 2
    @kesh The idea is actually the same if you pay close attention. You want to perform a behavior depending on the value that was chosen (In the other question, it was creating an object. In this question, it's calling a function with an argument). Rather than check the value and call a function based on the value, simply give the value the function, then call it from the value. Switching which value is used switches which behavior is performed. That is the definition of [polymorphism](https://en.m.wikipedia.org/wiki/Polymorphism_(computer_science)). – Vince Aug 20 '16 at 07:51
  • Incidentally, `TriggerResult` would be a much better name than `TriggerResultInterface`. – Kevin Krumwiede Aug 20 '16 at 07:55
  • 1
    As already explained by Vince, please don't ask a new question for every switch-case in your code base –  Aug 20 '16 at 07:55
  • 1
    Another thing you could look at is the Visitor pattern. – user207421 Aug 20 '16 at 08:00

1 Answers1

4

One of the solution is to use polymorphism to handle triggers differently. For instance, you could declare the Trigger interface and have several implementations. In this case, when you need a new trigger type, you just implement this interface and don't touch the existing code:

public interface Trigger {
    TriggerResultInterface execute(TriggerEventHelper eventHelper);
}

public class MetaTrigger implements Trigger {
    @Override
    TriggerResultInterface execute(TriggerEventHelper eventHelper) {
        // do meta trigger work here
    }
}

public class DataTrigger implements Trigger {
    @Override
    TriggerResultInterface execute(TriggerEventHelper eventHelper) {
        // do data trigger work here
    }
}

// ...

public TriggerResultInterface executeTriggerJob(TriggerEventHelper eventHelper) {
    eventHelper.getTrigger().execute(eventHelper);
}

In this case it will be impossible to add a new trigger type and not implement its behaviour.

If you need a default implementation, you can use a base class instead of the interface (in Java 8 you can add a default implementation right into the interface).

Andrew Lygin
  • 6,077
  • 1
  • 32
  • 37