mercredi 20 janvier 2021

Which of these two event bus architectures is the best?

I have wrote two architectures for an event bus to use in my application. The first one is based on an extensive use of interface's implementantions for every single event. It's probably the best in terms of performances, but it doesn't allow a more dynamic approach like in the second architecture. I also pasted the test classes so you can have a better understanding of how the two event bus works. This is the EventBus class of the first architecture (https://pastebin.com/pL54Emx2):

class EventBus {
 
    private val map = HashMap<KClass<Event>, PriorityQueue<EventQueueElement>>()
 
    fun dispatchEvent(event: Event){
        val list = map[event::class]
        if(!list.isNullOrEmpty()){
            list.forEach {
                it.listener.onEvent(event)
            }
        }
    }
 
    fun registerListener(eventListener: Listener, priority: EventPriority){
        val method = eventListener::class.members.first{it.name == "onEvent"}
        @Suppress("UNCHECKED_CAST")
        val eventClass = method.parameters[1].type.classifier as KClass<Event>
        var list = map[eventClass]
        if(list.isNullOrEmpty()){
            list = PriorityQueue<EventQueueElement>()
        }
        list.add(EventQueueElement(eventListener, priority))
        map[eventClass] = list
    }
 
    private class EventQueueElement(val listener: Listener, val priority: EventPriority = EventPriority.NORMAL) : Comparable<EventQueueElement>{
        override fun compareTo(other: EventQueueElement): Int {
            return priority.compareTo(other.priority)
        }
    }
 
}

And this is its test class (https://pastebin.com/bJ8p40zD):

class MyEventListener : Listener {
    override fun onEvent(event: Event) {
        print("test")
    }
}
 
internal class EventBusTest {
 
    @Test
    fun registerListener(){
        val myListener = MyEventListener()
        val eventBus = EventBus()
        assertDoesNotThrow {
            eventBus.registerListener(myListener,EventPriority.NORMAL)
        }
    }
 
    @Test
    fun dispatchEvent(){
        val myListener = MyEventListener()
        val eventBus = EventBus()
        eventBus.registerListener(myListener,EventPriority.NORMAL)
        val event = Event()
        assertDoesNotThrow {
            eventBus.dispatchEvent(event)
        }
    }
}

Instead, with the second architecture I can create infinite methods inside the same Listener implementation, but they must have the annotation @EventHandler and only have one parameter of type Event.
Here's the second implementation of the EventBus (https://pastebin.com/3KmQYLab):

class EventBus {
 
    private val map = HashMap<KClass<Event>, PriorityQueue<EventQueueElement>>()
 
    fun dispatchEvent(event: Event){
        val list = map[event::class]
        if(!list.isNullOrEmpty()){
            list.forEach { element ->
                val listener = element.listener
                listener::class.members.forEach{ method ->
                    if(method.hasAnnotation<EventHandler>() && method.parameters.size == 2){
                        @Suppress("UNCHECKED_CAST")
                        if(method.parameters[1].type.classifier as KClass<Event> == event::class){
                            method.call(listener, event)
                        }
                    }
                }
            }
        }
    }
 
    fun registerListener(eventListener: Listener, priority: EventPriority){
        val hashSet = HashSet<KClass<Event>>()
        eventListener::class.members.forEach{method ->
            if(method.hasAnnotation<EventHandler>() && method.parameters.size == 2){
                @Suppress("UNCHECKED_CAST")
                val eventClass = method.parameters[1].type.classifier as KClass<Event>
                if(!hashSet.contains(eventClass)) {
                    hashSet.add(eventClass)
                    var list = map[eventClass]
                    if(list.isNullOrEmpty()){
                        list = PriorityQueue<EventQueueElement>()
                    }
                    list.add(EventQueueElement(eventListener, priority))
                    map[eventClass] = list
                }
            }
        }
    }
 
    private class EventQueueElement(val listener: Listener, val priority: EventPriority = EventPriority.NORMAL) : Comparable<EventQueueElement>{
        override fun compareTo(other: EventQueueElement): Int {
            return priority.compareTo(other.priority)
        }
    }
 
}

and of its test (https://pastebin.com/jaf9BUSC):

class MyEventListener : Listener {
 
    @EventHandler
    fun onEvent(event: RandomEventOne) {
        println("test")
    }
 
 
    @EventHandler
    fun onEventTwo(event: RandomEventTwo){
        println("test2")
    }
}
 
internal class EventBusTest {
 
    @Test
    fun registerListener(){
        val myListener = MyEventListener()
        val eventBus = EventBus()
        assertDoesNotThrow {
            eventBus.registerListener(myListener,EventPriority.NORMAL)
        }
    }
 
    @Test
    fun dispatchEvent(){
        val myListener = MyEventListener()
        val eventBus = EventBus()
        eventBus.registerListener(myListener,EventPriority.NORMAL)
        val event = Event()
        assertDoesNotThrow {
            eventBus.dispatchEvent(event)
        }
    }
}

Which architecture is the best in your opinion?





Aucun commentaire:

Enregistrer un commentaire