0

I am implementing a trigger system. There are objects that take the role of observers (this is not strictly an observer pattern because the observers are inter-dependent, see my question here). They register to be triggered during certain events (by being added to the list of that event) and provide the functionality when such an event happens (by implementing the appropriate method).

I have the following interface:

public interface Triggerable {

    public boolean triggerOnEvent1();
    public boolean triggerOnEvent2();
    public boolean triggerOnEvent3();
    // etc.
}

and "corresponding" lists bundled in this class:

public class TriggerManager {

    static List<Triggerable> objectsToTriggerOnEvent1;
    static List<Triggerable> objectsToTriggerOnEvent2;
    static List<Triggerable> objectsToTriggerOnEvent3;
    // etc.

    // Getters for the lists: getObjectsToTriggerOnEventx()
}

I am using an adapter pattern here, which I think is irrelevant to the question, but it makes the code easier to read:

public abstract class AbstractTrigger implements Triggerable {

    @Override
    public boolean triggerOnEvent1() { return false; }

    @Override
    public boolean triggerOnEvent2() { return false; }

    @Override
    public boolean triggerOnEvent3() { return false; }

    // etc.
}

This is an example of a "trigger":

public class TriggerExample extends AbstractTrigger {

    public TriggerExample () {

        // register to specific events
        TriggerManager.getObjectsToTriggerOnEvent2().add(this);
        TriggerManager.getObjectsToTriggerOnEvent6().add(this);
    }

    @Override
    public boolean triggerOnEvent2() {
        // do something
        return true;
    }

    @Override
    public boolean triggerOnEvent6() {
        // do something else
        return true;
    }
}

When an event happens somewhere, it is notified to all registered "observers" using this iteration:

for (int i = 0; i < TriggerManager.getObjectsToTriggerOnEventx().size(); i++) {   // x stands for any of the above numbers
    Triggerable triggerable = TriggerManager.getObjectsToTriggerOnEventx().get(i);
    if (triggerable.triggerOnEventx()) {
        // do something
    }
}

The implementations of triggerOnEventx vary greatly and themselves can cause triggers with the loop construct above.

My concerns are twofold:

  1. I need to maintain the same data twice - for every method I add/remove from the interface (and the adapter), I need to add/remove a list. Reduces maintainability.
  2. I need to be careful not to use the wrong combination of a list and a method in the iteration - iterating on listx will always call methodx on its contents. Bug prone.

One thing I thought of doing is creating a Map<List, Method> in order to solve point 2. The thing is that it doesn't solve 1 and that it uses reflection, which comes with its own problems (like getting the method by name, which is also bug prone).

I can modify both of the types above and create new things, so the solution doesn't have to be limited to the above structure. What is a good design that suits this?

  • There's absolutely a much better design than this, but it would help if we knew what these abstract things are. Can you insert more domain-specific type mames so that we know e.g. whether the methods correspond to rip(), mix(), burn() or rather to filterByName(), filterByAge(), filterBySize()? That would make it much easier to see which element has to go where. – Kilian Foth May 11 '16 at 13:24
  • So list1 items should always use method1, list2 items should always use method2, and so on..? Am I understanding that correctly? – Jason Tyler May 11 '16 at 13:27
  • @KilianFoth I added more details. Please see if I can further clarify anything for you. – user1803551 May 11 '16 at 14:16
  • @JasonTyler Correct, though the same item can be in more than one list. See my edited post. – user1803551 May 11 '16 at 14:17
  • The straightforward solution when you have repeated names (i.e. xyz1, xyz2, xyz3), is to design a first-class entity around the individual that is being repeated. In other words, you should work on making a first-class concept (e.g an interface) out of the notion of a single, individual trigger; this will address your naming issues. Then when you have that, create manager concept that is a collection or an assembly of those individual triggers; this will address your pairing problems. Is there a reason this won't work here? – Erik Eidt May 11 '16 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.