1

I have an event handler that creates objects for which it needs data from both before and after an event. The event handler itself implements Selenium's WebDriverEventListener interface and gets registered accordingly. That is, I have no control over how the class is being invoked.

In order to create the objects, I use a mutable field. Here is a simple example (in Java):

public class MyEventHandler {

    private MyType myMutableField;

    public void beforeEvent(DataA a) {
        myMutableField = new MyType();
        myMutableField.setA(a);
    }

    public void afterEvent(DataB b) {
        myMutableField.setB(b);
        // Do something with myMutableField...
    }

}

To prevent inappropriate method invokations (e.g. consecutive invokations of beforeEvent or afterEvent), I do the following:

public class MyEventHandler {

    private MyType myMutableField;

    public void beforeEvent(DataA a) {
        if ( myMutableField != null ) {
            throw new IllegalStateException();
        }
        // ...
    }

    public void afterEvent(DataB b) {
        // ...
        myMutableField = null;
    }

}

I have also implemented mechanisms such that setters can be invoked only once.

However, I'm not really happy with this design approach and I wonder if there is a pattern that helps me to a) avoid the mutable field and/or b) make MyType immutable.

  • 1
    I’m wondering, how do you invoke a method in an event handler, before the event happens? I mean, the definition of an event is that something has happened and there is a class that handles the thing that has happened. So how can there be something inside this class that should be called before the thing happened? – Rik D Dec 11 '18 at 22:52
  • 1
    @RikD could be wrapping someone else's event handler. Or you could have multiple events in sequence, like serverAboutToReboot / serverRebooting / serverRebooted – immibis Dec 12 '18 at 1:24
  • 1
    If the event handler knows the sender, it could unregister itself before registering a second event handler that is constructed with DataA. But you might also be better served by looking into a proper state machine library. – Alex Reinking Dec 12 '18 at 6:25
  • serverAboutToReboot / serverRebooting these are not events because they are still happening!. These are 'states", not events. – Laiv Dec 12 '18 at 10:39
  • @RikD "before" can have various meanings. For instance, when you have some sort of graphics library that uses a scene graph, you may want to consume events before they are dispatched down that graph ("event capturing"), whereas other forms of handlers are only notified when the event is returned to the graph's root ("event bubbling"). – beatngu13 Dec 12 '18 at 10:57
1

I have also implemented mechanisms such that setters can be invoked only once.

Why don't' we change the rationale? Let's make it executable "at least once" and make it idempotent so that we don't have to be worried about the number of subsequent calls.

public class MyEventHandler {

    private MyType myMutableField;

    public void beforeEvent(DataA a) {

        if ( stateBeforeEvent ) {
            runMyCode(a);
        }
    }

    public void afterEvent(DataB b) {
         if (stateAfterEvent) {
             runMyCode(a,b);
         }
    }

}

For brevity, I didn't give any implementation for stateBeforeEvent and stateAfterEvent. How to obtain and hold both states (conditions) depends on your resources at hand.

Note that I have removed the exception throwing. Unless you need to break the whole execution path I found it to be quite "defensive". Too strict. If you still need to throw an exception, we need to make some changes

public class MyEventHandler {

    private MyType myMutableField;

    public void beforeEvent(DataA a) {

        if ( !stateBeforeEvent ) {
            throw IllegalStateException("BeforeEvent has been already executed!");
        }
       runMyCode(a);
    }

    public void afterEvent(DataB b) {
         if (!stateAfterEvent) {
           throw IllegalStateException("AfterEvent has been already executed!");
         }
         runMyCode(a,b);
    }

}

However, I'm not sure if we can label these methods as "idempotent" any more. Probably not.

Related links

  • +1 although it doesn't remove the mutable field, I like the approach. – beatngu13 Dec 12 '18 at 20:43
  • Perhaps, you could create MyType only when you have both Data objects (A and B). Can not you wait untill afterEvent is called? new MyType (a,b)? – Laiv Dec 12 '18 at 21:38
  • Instead of using MyType as a mutable field, I could use a field of type DataA and then create an immutable version of MyType in afterEvent. But the reference of that field can't be final either. – beatngu13 Dec 12 '18 at 21:45
  • Inmutable and final are different things. You could use a builder and make builder's methods idempotent. At the end, when build() is called, a single MyType is returned. Just make MyType' setters protected and let the builder to be the only one allowed to call these methods. – Laiv Dec 12 '18 at 21:58
  • 1
    "Immutable and final are different things." Yep, see the original question: a) final field, b) immutable MyType. Regarding the builder: I already tried a step builder, which addresses the immutability of MyType. – beatngu13 Dec 12 '18 at 22:04
0

It seems your using code will look like this:

MyEventHandler meh = new MyEventHandler();
meh.beforeEvent(a);
DataB b = someCalculation();
meh.afterEvent(b);

When it could have looked like this:

MyEventHandler meh = new MyEventHandler(a);
DataB b = someCalculation();
meh.afterEvent(b);

Done that way everything can be immutable. This is the same trick closures pull off in functional languages. This is just how the trick looks in an object oriented language.

In either case the trick is basically saving something you know now to use with something you'll learn later. Possibly somewhere else.

Disclaimer: I'm only using these names so you can match them to your original code. Please improve them before you use this.

  • Is not the handler itself a whole "AfterEvent"? Handlers come into play when "something" already happened. Given your answer, I wonder if we could not just make something like new MyEventHandler(a, someCalculation()) , I don't why but afterEvent and beforeEvent seems both code smell to me. – Laiv Dec 12 '18 at 10:48
  • Unfortunately, I have no control over how the class is being invoked (edited the question accordingly). Sorry, should have mentioned this earlier. – beatngu13 Dec 12 '18 at 10:59
  • @beatngu13 I've noted your edit but still don't see your using code. Show me what I'm working with. – candied_orange Dec 12 '18 at 20:05
  • What exactly would you like to see? I have tried to simplify the code example as good as possible, and the handler is all that is under my control. I just hand the implementation over to the Selenium framework and it gets invoked whenever the corresponding events occur. – beatngu13 Dec 12 '18 at 20:18

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.