Java’s delegation event model

Extremely Extensible Software Design (Part 2)

Update

Benji Shults
4 min readJul 14, 2018

--

I’ve created a github repo with better, simpler, and more complete code and a better write-up of the ideas. The ideas Part 1 are laid out there. The ideas in this, Part 2, article are in another file in that repo.

What is “extensible code?”

Recall from my previous article the way I think about extensible code:

Code is extensible if a developer need not edit any existing code other than configuration code in order to add a new feature.

In that article, we saw a pattern that allowed us to extend an application without editing any existing code.

In my next article, I will explain some reasons I think this is a good definition of “extensible code”.

In this article, we will learn another pattern that allows our code to be extended by editing only configuration (or “glue”) code.

The common thread in extensible code: polymorphism

In OOP, polymorphism is a language feature that allows the behavior of a method call to vary depending on the run-time type of the receiver. This feature is used in such design patterns as the State Pattern, the Template Pattern, the Strategy Pattern, and many more.

When this kind of design is used, adding a feature means implementing an existing interface then editing the configuration code to add your implementation to the application.

There are many patterns that use polymorphism to enable extremely extensible code. (We saw a nice one in my previous article.) This article will focus on what is probably my favorite, mind-blowing pattern for extensible code.

The delegation event model

This pattern is commonly-used in UI frameworks but if you understand the pattern, you can use it to solve problems in a lot of different domains. This pattern is actually a combination (or culmination) of many patterns.

When we are writing a UI, we often want a button click to result in our code being executed. Here are the problems we have to overcome to make this work:

  • The Button class knew nothing about the class we are writing when it was compiled! How can it execute our code?
  • We don’t want the business-logic code we write to have a compile-time dependency on the Button class! That’s poor separation of concerns.
  • Nor can we (nor do we want to) edit the code of the Button class in the framework we are using.

How do we solve these problems? The delegation event model. (Details below.)

Problem

Let’s get back to the problem we were solving in my previous article.

This time, we will implement the endpoint that allows a user to create a comment. The payload of the POST will look like this:

We want our endpoint implementation to do the following:

  • ensure that the user is a member of the comment user group
  • persist the comment to the COMMENTS table
  • allow itself to be extended as later features are added (e.g., parse out mentions, parse out hashtags, filter out naughty words, etc.)

Solution

We’ve already seen how to ensure that the user is a member of the identified comment user group. We are not going into much detail about pushing to the database. As usual, we are going to focus on the pattern of extensibility.

First, let’s implement the delegation event model so that later, anyone can easily extend our code with whatever code they want without editing existing code other than configuration.

Event and listener

First, we want a new “event” type. We will simply use the Comment class.

Abstraction Note: The “event” type does not have to extend Java’s EventObject and you don’t even have to think of it as an “event”. This just represents whatever interested parties might need to know. It can be whatever class fits your domain.

Then, we need an interface that folks will need to adapt to.

Abstraction Note: Here, we have not made it extend Java’s EventListener. That is obviously not required. My interface does not have to extend anything and it doesn’t have to be called “listener”. Its method does not have to take an event as its argument. Call it what makes sense and pass in whatever argument(s) make sense.

Event source

Our event source class will be the CommentController class that will handle the POST.

We will start with the implementation of the delegation event pattern then we will implement the actual endpoint.

Notice what will happen when the fireCommentAddedEvent method is called. Each registered listener will be notified about the new comment!

Abstraction Note: All I have to do to make an extensible event source, is to allow listeners to register. When the event occurs, (in this case, a comment is added but it could be anything) then all of the listeners that have registered are notified.

Finally, here is the implementation of the POST endpoint:

Conclusion

What do we do when a new requirement comes in and we need to parse comments for mentions and send those to another endpoint?

We simply write an implementation of the CommentAddedListener interface that implements that requirement. To add this feature to our application, we simply edit our configuration (glue) code. Assuming we have a bean named mentionsParser that implements CommentAddedListener with the new requirements, and a bean named commentsController of type CommentsController, then we add a line to our configuration code:

commentsController.addCommentAddedListener(mentionsParser);

When a new requirement comes along, we just add another listener!

Once again, we have written code that is extremely extensible! In order to add new features, one must only edit existing configuration code (and, of course, your new classes.)

Read the next part of the series to learn some of the reasons I think this is a good definition of “extensible code.”

Happy coding!

--

--

Benji Shults

Staff Software Engineer at SmartThings with a PhD in Mathematics and Artificial Intelligence from UT-Austin