Summary: The Mediator Design Pattern is demonstrated in a Java example — i.e., this is a Java Mediator Pattern example.
In object oriented programming, "loose coupling" between objects is a good thing — it promotes flexibility and reusability. However, in some applications, such as GUI programming, you can easily run into circumstances where many objects seem to need to know about each other. For instance, in a Java/Swing GUI, when you make a change in one part of a dialog, window, or frame, other parts of the GUI may also need to be updated, or otherwise respond to this change.
There are several ways to update other GUI components like this, but tightly coupling the components by letting each component know about the other component is not a good approach.
A better way to achieve the desired effect while also promoting loose coupling is to implement the Mediator design pattern. In this article I'll discuss the Mediator Design Pattern in a Java example.
The Mediator Design Pattern in Java
The Mediator Design Pattern is a very good solution to the problem described above. In short, instead of tightly coupling your GUI components together, you create a "mediator" object, and that class is the only object that is aware of all the other objects, and all message-passing goes through this mediator.
Without knowing it, I began using this pattern 10-12 years ago while trying to solve this problem in my own GUI applications. I had gotten in the habit of using Java GUI builder tools like JBuilder and Visual Cafe. These tools seemed to make Java GUI development easier, but in reality, several things they did made long-term GUI maintenance more difficult by tightly coupling the GUI components. Every change to my GUI applications seemed very painful.
I finally got away from this problem when I learned about the MVC Pattern, specifically the MVC Pattern diagram shown here. When I learned to follow a pattern of having a controller for major process in a software system, I inadvertently started implementing the Mediator pattern.
A Java Mediator Design Pattern example
I'll demonstrate the Mediator pattern by discussing a Java application I wrote many years ago named FPTracker. I created this application to let me count Function Points for applications, back in my days as a Certified Function Point Specialist (CFPS). The main window of the FPTracker application looks like this:
(The main window of the FPTracker application.)
As you can see, the application has four primary tabs with these labels:
- Process Groups
In my Java code I created four main controllers that corresponded to each of these tabs, specifically these:
For each controller there is also a main corresponding GUI panel:
Java Mediator pattern in the "Processes" tab
Getting to my Java Mediator pattern example, let's look at just the "Processes" tab. As you can see from the figure above, the Processes panel allows these four main actions:
- Add Process
- Clone Process
- Edit Process
- Delete Process
The dialog that appears when a user clicks the "Add Process" button looks like this:
(The "add process" dialog.)
Without getting into all the details, there are a few important things going on here:
- Entering data in this dialog and pressing Apply or OK updates the main Processes panel.
- The Process Group and Process Type drop-down boxes have effects on behind the scenes calculations.
- I want to let the user either type in the number of FTRs and Number of DETs, or let them click those buttons to the right of each field, which allow for much deeper logic and processing.
- The complex functionality behind the "FTRs..." and "DETs..." buttons that has ripple effects in my Java code, including the Complexity field shown, as well as the underlying data model.
Tying all these things together, and also trying to keep my implementation flexible and loosely coupled, I created a design based on the Mediator design pattern that can be described like this:
- I created an AddProcessDetailsPanel, with the components shown.
- I created an AddProcessDescriptionPanel, which is not shown.
- I created a ProcessFTRsDialog, which again is fairly complicated.
- I created a ProcessDETsDialog, which is also fairly complicated.
- To implement my Java Mediator Design Pattern, I created an AddProcessController, which is the mediator between all these GUI components.
In this design, the AddProcessDetailsPanel doesn't know anything about the AddProcessDescriptionPanel, and they don't know anything about the ProcessFTRsDialog or the ProcessDETsDialog. Beyond that, although the Complexity field relies on the Process Type, No. FTRs, and No. DETs fields, they also don't know about each other. The only objects that knows about all of these fields is my Java mediator object, the AddProcessController.
In short, the AddProcessController knows about all these GUI components, and has change listeners attached to the GUI fields it needs to listen to, and it also has get/set methods to all these fields.
Why this is good
The reason this design is good is that I can change anything I want at any time with minimal effect on the other system components. I can redesign my dialog, change the DETs and FTRs implementation, and pretty much anything else, and the only changes needed are to (a) those components and (b) my mediator class.
On the other hand, if each GUI element was tightly coupled to every other GUI element, well, any change would be a significant rewrite, and I'd be tracking down event interactions for a long time.
I could write much more about this code, and in fact I already have. If you'd like to know a lot more details about this Java Mediator Pattern implementation, please see my Java Model View Controller (MVC) tutorial/example.
Benefits of the Mediator Pattern
As mentioned above, the benefits of the Mediator Design Pattern include:
- A large number of objects are now loosely coupled.
- The Mediator is the only object that knows about all of the other objects.
- If/when you redesign your application or user interface, the only components that need to be changed are the objects you plan to change, and your Mediator class.
- New classes can be added without affecting existing classes.
- You eliminate a lot of "spaghetti code", if/then statements, and unnecessary relationships between "peer" objects.
- The Mediator pattern solves the problem where Command objects seem to need to know too much about other objects in the system. (That is, they are tightly coupled to the other objects.)
Consequences of the Mediator Pattern
The only negative of the Mediator Design Pattern is that you have to be careful not to create something referred to as a "God object", which is what happens when your Mediator class grows way too large. That could happen in the example above if I only had a ProcessController, but because I have a ProcessController which defers to smaller controllers like the AddProcessController and DeleteProcessController, this does not happen.
One simple refactoring fix to having a "God object" is to refactor your code as I just described; break a large mediator class down into smaller classes, where each mediator only knows about the objects it really needs to know. As a practical matter, this is when I usually break out the UML, or at least CRC cards, so I can think about how this system needs to be refactored.
A second refactoring fix is to make sure your code logic is in the right places. Think of your mediator class as being a traffic cop or coordinator between all these different objects, and also know that it's not going to be reusable if you switch from a Java Swing GUI app to a Java web app or mobile phone app. As you switch from Java GUI to web app to mobile app, you'll probably have to throw out your Java GUI as well as your Java mediator class, but, the logic behind these classes -- the business logic -- will be the same in each environment. Therefore, where should this business logic be kept? When you answer this question, it will also help to keep your Java mediator pattern class from becoming a "God class".
Related design patterns
The Observer pattern is similar to the Mediator pattern. The primary difference is that the Observer pattern works by sending notifications from subject classes to their observer classes, while the Mediator pattern is involved not only in observing, but also handling communication between objects.
Command pattern objects will probably also be found in Mediator pattern objects. In looking at the Java Mediator pattern example above, if you think that each button and menu item is wrapped in a Command object, this is easy to see.
Mediator Design Pattern in Java - Summary
I hope this discussion of a Java Mediator Design Pattern has been helpful. If you have any questions or comments, please leave a note below, and I'll be glad to reply.