3

I would like to generalise some code, which implements event handling in a naive way. I have multiple types of events, all share the same base attributes along with attributes that are unique to each type. The events are coming in in a Json format with an attribute that denotes their type. for example :

{id:1, baseAttribute1:X1, baseAttribute2:Y1, type:eventA, eventAattribute1:Z, eventAattribute2:Q}

{id:2, baseAttribute1:X2, baseAttribute2:Y2, type:eventB, eventBattribute1:ZZ, eventBattribute2:QQ}

I am trying to write something generic that will let me implement common handling seperately from the specific one but if i use some base object BaseEvent that eventA and eventB extend from - then, in a factory method for example, i am losing the type of the event and cannot forward it to the correct call so i can't add doStuff(EventA a) and doStuff(EventB b) because the object is of type BaseEvent.

3
  • 3
    Base class with id, baseAttribute1, baseAttribute2, type. Subclasses with the event-specific stuff. Commented Jun 25, 2019 at 12:42
  • Your example doesn't contain attributes that are "unique to each type". Commented Jun 25, 2019 at 12:54
  • If you have multiple methods handleEvent(BaseEvent) that are overloaded you could get a java.lang.reflect.Method accordingthe type using Reflection and invoke the Method with the event. Commented Jun 25, 2019 at 13:49

2 Answers 2

2

I had to implement a similar thing earlier and here's what I did.

1. JSON to POJO

You can make use of jackson json mapper library to map your polymorphic json to pojo. You'll need to have a base class with derived classes for specific event types. Jackson inheritance

2. Event handlers and Chain of responsibility design pattern

Make use of chain of responsibility design pattern. Have event handler classes corresponding to each of your event types. Each event handler should have a safety check method which should return true/false based on whether it can handle that particular type of event or not. Another method in these event handlers should have code to act on that event. If the event handler is not capable of handling that particular event type, it should pass on to next event handler. Chain of responsibility design pattern

Alternatively, you can also explore Observer design pattern. Observer design pattern

Example (Chain of responsibility design pattern)

Following is a short example demonstrating the same. You can refactor it by having a factory create chain for you and move instanceof checks and invoking next handler in a wrapper method of base handler class.

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone {
    public static void main (String[] args) throws java.lang.Exception {
        DogHandler dogHandler = new DogHandler(null);
        CatHandler catHandler = new CatHandler(dogHandler);

        AnimalHandler animalHandler = catHandler;

        Dog d = new Dog();
        Cat c = new Cat();

        animalHandler.apply(d);
        animalHandler.apply(c);

    }

    static abstract class Animal {
        public void print() {
            System.out.println("Animal");
        }
    }

    static class Dog extends Animal {
        public void print() {
            System.out.println("Dog");
        }

        public void dogMethod() {
            System.out.println("Dog specific method");
        }
    }

    static class Cat extends Animal {
        public void print() {
            System.out.println("Cat");
        }

        public void catMethod() {
            System.out.println("Cat specific method");
        }
    }

    static abstract class AnimalHandler {
        AnimalHandler nextHandler;

        public AnimalHandler(AnimalHandler nextHandler) {
            this.nextHandler = nextHandler;
        }

        public abstract void apply(Animal a);
    }

    static class DogHandler extends AnimalHandler {

        public DogHandler(AnimalHandler nextHandler) {
            super(nextHandler);
        }

        public void apply(Animal a) {
            if (a instanceof Dog) {
                Dog d = (Dog) a;
                d.dogMethod();
            } else {
                nextHandler.apply(a);
            }
        }
    }

    static class CatHandler extends AnimalHandler {

        public CatHandler(AnimalHandler nextHandler) {
            super(nextHandler);
        }

        public void apply(Animal a) {
            if (a instanceof Cat) {
                Cat c = (Cat) a;
                c.catMethod();
            } else {
                nextHandler.apply(a);
            }
        }
    }
}

Note: If you have multiple event handlers acting on a event sequentially, go for chain of responsibility. If there is a single event handler acting on a given event, better have a Map<Class, EventHandler> as mentioned in a comment below my answer or Observer pattern.

Sign up to request clarification or add additional context in comments.

4 Comments

The solution with the chain is not very resource-efficient for a large amount of handlers. Consider a Map<Class,AnimalHandler>.
Agreed, my solution fits a use case more wherein you need to have multiple handlers acting on an event. Better I add a line about this in my answer.
I've added that solution(and others) as an answer.
I dont really like the way COR define as this catHandler = new CatHandler(dogHandler);
2

If you want to have one Handler class with multiple Methods:

You can simply use a huge if-else or use Reflection

if-else:

public void handle(Event event){
    if(event instanceOf(EventA)){
        handleEventA();
    }
    else if(event instanceOf(EventB)){
        handleEventA();
    }
    //...
    else{
        handleDefaultEvent();
    }
}

Reflection:

package com.stackoverflow.q56754275.single_class;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class HandlerController {
    private Handler handler;
    public HandlerController(Handler handler) {
        this.handler=handler;
    }
    @SuppressWarnings("unchecked")
    public void handle(Event event) {
        Class<? extends Event> eventClass=event.getClass();
        Class<?> handlerClass=handler.getClass();
        Method handlerMethod=null;
        while(eventClass!=null&&!eventClass.equals(Object.class)) {

            try {
                handlerMethod = handlerClass.getMethod("handle", eventClass);
                handlerMethod.invoke(handler, event);
                break;
            } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
            }
            Class<?> superCl=eventClass.getSuperclass();

            if (superCl!=null) {
                eventClass=(Class<? extends Event>) superCl;
            }

        }
    }
}
package com.stackoverflow.q56754275.single_class;

public interface Handler {
    void handle(Event event);
}
package com.stackoverflow.q56754275.single_class;

public class Event {

}
package com.stackoverflow.q56754275.single_class;

public class SampleEventHandler implements Handler{

    @Override
    public void handle(Event event) {//default Handler

    }
    public void handle(EventA event) {//Handler for EventA

    }

    private static class EventA extends Event{

    }
}

If you want hava a class for each handler:

You can create a Map<Class<? extends Event>,Handler> in order to find the Handlers of Events.

package com.stackoverflow.q56754275.multiple_classes;

import java.util.HashMap;
import java.util.Map;

public class EventController {
    private Map<Class<? extends Event>, Handler> handlers=new HashMap<>();
    @SuppressWarnings("unchecked")
    public void handle(Event event) {
        Class<? extends Event> cl=event.getClass();
        while(cl!=null&&!handlers.containsKey(cl)) {
            Class<?> superCl=cl.getSuperclass();
            if (superCl==null||superCl.equals(Object.class)) {
                cl=null;
            }
            else {
                cl=(Class<? extends Event>) superCl;
            }
        }
        if (cl != null) {
            handlers.get(cl).handle(event);
        }
    }
    public void addHandler(Class<? extends Event> eventToHandle, Handler handler) {
        handlers.put(eventToHandle, handler);
    }

package com.stackoverflow.q56754275.multiple_classes;

public class Event {

}
package com.stackoverflow.q56754275.multiple_classes;


public interface Handler {
    void handle(Event event);
}

How to create a Handler?

package com.stackoverflow.q56754275.multiple_classes;

public class SampleEventHandler implements Handler{

    @Override
    public void handle(Event event) {
        SampleEvent se=(SampleEvent) event;//if this is only for Events of type SampleEvent
        System.out.println(se);
    }

}

How to create an Event?

package com.stackoverflow.q56754275.multiple_classes;

public class SampleEvent extends Event {
    @Override
    public String toString() {
        return "SampleEvent";
    }
}

How to add a handler to the Controller(e.g. SampleEventHandler for SampleEvents ?

controller.addHandler(SampleEvent.class, new SampleEventHandler());

[NOTE]

It is possible to execute multiple handlers if you use Collections for handlers and iterate through them.

2 Comments

It's a nice idea, but i feel that once you go SampleEvent se=(SampleEvent)event , than you broke the oop design and its not very generic since there is no logical connection between SampleEventHandler and SampleEvent other than that one line. i'll try to see if Introducing generics to this might add some value
It is possible to automatically assign the handler in a constructor or a factory method if the handler. With this you still need to cast but you are sure that the type is right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.