About Gio
I am a torrent of ingenuity (or insanity) with a myriad of innovations (sometimes fallacies) and a wealth of inspiration (possibly naiveté). My name is Gio Carlo Cielo Borje and I like puffer fish because they're just cooltalkin', highwalkin' and fastlivin'.
I'm also nineteen and a current student at UC Irvine for Computer Science.
MinDispatch: Event-Driven Framework In Java Part 2
From the last post in this series, we developed a fixed, event-driven chat simulation. In this post, we will extend this example by refactoring. The objective of this tutorial is to teach effective design patterns in an event-driven model. First we will begin by designing the structure and behavior of the user and chat to describe our application. Second, we will bind the aforementioned chat state to the event handlers to fix constant parameters. Finally, we will use an event queue for separation of concerns.
Continuation
We will continue with our previous example which you can find on GitHub. It will be easier to follow along with the source code.
Beware that I will no longer provide a full example of the code. It is expected that you are aware of where the highlighted code modifications occur because the code file has grown large.
Structural Design
We begin by specifying the structural requirements of our chat. That is, we must specify the fields associated with our two primary data structures:
UserandChatState.User State Structure
We begin with the atomic data structure, the
User. A user in a chat has a name.Simple enough.
Chat State Structure
Next,the
ChatStatemust model the configuration of a chat room: it should maintain the list of users currently in the chat.Now to move on to the behaviors of our design.
Behavioral Design
The structure of our design has been outlined. Now we must enable interactions between these structures relative the possible events that may occur. Recall the events of a chat application:
Both the
ChatandUsermust respond to these events accordingly. The following two sections will describe their behavioral implementation respectively.Chat State Behaviors
Beginning with the
ChatStatethis time, we must handle each of the aforementioned events. Specifically, we must support the following operations:Broadcasting must be handled in a special manner. For broadcasting, we must specify our recipients: all users should receive a copy of an event dispatched. Since we have outlined the design, consider the following interface which expresses our intent,
The implementation is relatively straightforward: the
userslist maintains the current users in the chat and events can be broadcasted to subsequent users.The
ChatStateis responsible for broadcasting events to each of the registered users. So, theChatStatemust broadcast the message to individual users so that they may handle the messages individually. See the diagram for more information.Event broadcasting
When we construct the event queue, the
ChatStatewill act as an event forwarding mechanism forUserMessageevents.Beware that there is a dependency the above code. The
broadcastmethod dispatches events to users by calling theUser.dispatchmethod which doesn’t exist yet. So, let us continue onto theUserbehaviors.User State Behaviors
We will outline the behavioral implementation of our
Userclass now. Since the user is capable of receiving events, we should demultiplex the incoming events and handle them appropriately. Specifically, we want to know if a user received a message. Consider the implementation then:Take a look at the
broadcastmethod. The type of the event argument is compared againstUserMessage.class. This if-ladder is an example of event demultiplexing.Event demultiplexing
When demultiplexing events, we route events to their respective handlers. Specifically, we route all of the
UserMessageevents to theprocessMessagehandler and ignore the rest (arrival and departure are ignored). Once the events have been handled after demultiplexing, the behaviors of the data structure are complete.Binding Chat State to Event Handlers
Unfortunately, now that a
ChatStateexists, we must pass the object, as a parameter, to the each of the event handlers so that they may change the state of the object. Consider the event handler setup forUserArrivalPassing state along with each event may cause significant code duplication as well as unnecessary runtime overhead. With the current design of event handlers, each of the previously designed handlers to the user events,
UserArrival,UserDepartureandUserMessagemust store a reference to theChatStatethat they operate on.There exists a solution which removes the code duplication and the runtime overhead. We can push the responsibility of maintaining state to the event handlers by binding the
ChatStateto a custom event handler. We know that this is feasible becauseChatStateis the same throughout the execution of this simulation.Application-specific Chat Handlers
We will implement our own event handlers,
ChatHandler, specifically for handling chat-specific events on aChatState. Simply, this custom handler should fix the parameter common to all of our handlers,ChatState.Afterwards, we may access the state of the chat for each subsequent
ChatHandler. So, the handler registration will be slightly different with an inherited handler.The state of the chat is updated in the above event handlers using the behavioral design that we have previously specified. Hence, we have effectively decoupled the state of the chat from the event dispatching.
Using an Event Queue
Next, we will utilize an event queue to separate concerns.
The event queue will enable us to separate the event dispatcher from the application-specific users and the chat state. That is, users should be unaware of the existence of a dispatcher especially when generating events themselves.
Conceptually, the event queue acts as a multiplexed channel which interleaves events from individual users since there is no particular order in which users may send messages. The event queue’s only concern is event multiplexing.
Event multiplexing
For now, we will simply use a simple
Queue<Event>in the Java standard library to express our intent. So, to instantiate this, we use ajava.util.LinkedList.Integration with Dispatcher
Since the
Dispatcheris responsible for dispatching events, we should dispatch all of the events in queue when flushing the buffer.Furthermore, notice that the event queue does not interact with the
ChatState. This is a highlight of separated concerns because event queues are application-independent.Integration with Users
Before dispatching events with users, we must connect users to the event queue. Simply, we enable each individual user to reference the event queue in the implementation.
Once users have a reference to the event queue, they are able to generate events. Specifically, we want to enable users to send messages to the chat, thereby sending a message to all other users currently in the chat.
Thus, users are now capable of sending messages without being aware of the event dispatcher and the chat state. Effectively, this is a highlight of modularity where modifications to a user’s capability in the system is independent of the modifications to the chat state and event dispatcher.
Testing the Simulation
Finally, the final source should be similar to my source code on GitHub. Now, we can test the simulation using the following
mainand hardcoded events:The following output should be produced:
Thus, our chat simulation is complete.
Conclusion
Effective application design coupled with an event queue makes modification of the code far easier simply because we have a separation of concerns and modularity. That is, modifications to our application-specific handlers or data structures are independent of modifications to the application-independent event-driven framework, MinDispatch framework on GitHub..
It is easy to see that using the MinDispatch framework significantly simplifies the design for an event-driven application by handling the application-independent work.
Further Reading
I recommend reading Douglas Schmidt’s collection of papers on event handling and concurrency. Specifically, the Reactor Pattern has significantly influenced the design of my framework.
Another Continuation
There are two paths we can take from here:
Decide. Comment your preference below.