fbpx

What is the mediator design pattern? The mediator behavioral pattern allows you to define complex object interactions while still keeping each object simple and unaware of the other objects.

Just open any user interface dialog box and notice how some controls become disabled when the state of other controls changes. Maybe there’s some text you need to enter before you can proceed. A well designed user interface will make this clear by disabling the Ok or Next button whenever the text field is empty.

The solution that this pattern describes involves a new class called the mediator which knows about all the other classes that need help with their relationships such as this text field and button. Each class just needs to refer to the mediator instead of trying to coordinate with the other classes directly.

You’ll want each of your classes to have a base class or interface with a method called notifyMediator. Whenever a class changes such as when the user types some text into a text field or deletes some text from the text field, it can call its notifyMediator method. As far as the text field is concerned, it’s done. This is simple code that can be used anywhere.

How did the text field know about the mediator? That’s the job of the mediator. The mediator will either create each control itself and then as part of the creation process, let each class know about the mediator, or it will let some other class perform the creation and then visit each class and call some method common to them all that lets the class know about the mediator. Because each class has a common interface, the mediator will be able to use this interface to inform the classes about the mediator.

Let’s look at what happens in the specific case where the user types something into the text field.

  1. Each key press that results in the text changing will cause the notifyMediator method to be called.
  2. This method then calls a changed method in the mediator and sends a pointer or reference to itself to the mediator.
  3. At this point, the text field has informed the mediator that something has changed and let the mediator know that it was the source of the change.
  4. The mediator is notified that a change has taken place and looks at the source of the change and sees that it came from the text field.
  5. The mediator makes a request back to the text field to get whatever text it currently contains.
  6. The text field responds to the method call by providing its text.
  7. Now that the mediator has the contents of the text field, it puts its special knowledge to work and checks the length of the string. If the string is empty, then the mediator disables the Ok button. And if the string has text in it, then the mediator enables the Ok button.

If you’d like to read the book that describes this pattern along with diagrams and sample code, then you can find Design Patterns at the Resources page. You can find all my favorite books and resources at this page to help you create better software designs.

Listen to the full episode or you can read the full transcript below.

Transcript

The basic description says that this pattern defines an object that encapsulates how other objects interact. This provides loose coupling between objects and lets you reuse them in other scenarios.

Have you ever noticed the special deals sometimes available in supermarkets? Usually, there’s a little tag next to an item that says, “Buy one, get one free.” Or sometimes, they’re more complicated like “Buy two of these items and get $1 off other specially marked items.”

Interactions between objects like this are common in software.

Just open any user interface dialog box and notice how some controls become disabled when the state of other controls changes. Maybe there’s some text you need to enter before you can proceed. A well designed user interface will make this clear by disabling the Ok or Next button whenever the text field is empty.

So whether you’re disabling controls or getting free chips, this is a common scenario that all developers face. What’s the best way to design a solution for this?

Here’s one way you should avoid.

The problem is this is often direct and simple for isolated and small cases. And it’s not until later that you realize the mistake. If the text field has a reference to the Ok button, then it can just enable and disable the button at the proper time. This works until you need to enable and disable another button. Then you change the code so the text field has a reference to both buttons. But then, the dialog needs more changes. It seems that the second button should be disabled whenever a new checkbox is checked.

It’s already starting to become a mess but you keep going down the same path. Just add a reference to the checkbox so the text field can read its value when deciding to enable or disable the new button. Oh, and now you need to have similar code in the checkbox to read the text field value too whenever the checkbox is changed because it can also enable or disable the new button.

As if things weren’t bad enough already, here’s where you really run into trouble.

You start work on a new dialog box with an entirely different control arrangement and realize you can’t use your customized text field class because it has all that extra code for the other dialog.

You need to keep your classes focused and unaware of all these special interactions so the classes can be reused in other scenarios where the interactions will be different.

Just think of the grocery store with the price specials. These specials change every week. There’s no way you can keep up with changes like this if the code for managing the interactions lives in each class directly.

I’ll explain a much better way that uses the mediator pattern right after this message from our sponsor.

( Message from Sponsor )

The solution that this pattern describes involves a new class called the mediator which knows about all the other classes that need help with their relationships. Now, instead of each class knowing about other related classes, each class just needs to refer to the mediator.

You’ll want each of your classes to have a base class or interface with a method called changed, or notifyMediator, or something similar. Whenever that text field gets some typing action, it can call its method to notifyMediator. As far as the text field is concerned, it’s done. This is simple code that can apply anywhere.

How did the text field know about the mediator though?

That’s the job of the mediator. The mediator will either create each control itself and then as part of the creation process, let each class know about the mediator, or it will let some other class perform the creation and then visit each class and call some method common to them all that lets the class know about the mediator. Because each class has a common interface, the mediator will be able to use this interface to inform the classes about the mediator.

In this design, the mediator can get a bit complicated because its job is to coordinate all the relationships between all the other classes. The mediator needs to keep each class instance separate and know what role each plays. The nice thing about this though is that the design won’t be any more complicated than the initial design with all the duplicated code and should be as simple as possible.

That doesn’t mean it’ll be easy especially for complicated relationships.

Just imagine how many relationships a large supermarket can have between possibly hundreds of thousands of products. You may find it easier to have several mediators if you have so many object relationships. Note that the vast number of items in a supermarket are not participating in relationships with other products. And the price specials tend to revolve around special manufacturer promotions. That’s why it may make sense to have different mediators especially when some promotions can start and end at different times.

If we go back to the dialog box example with the text field that needs to have text in order for an Ok button to be enabled, then what actually happens?

Let’s first take a look at the static relationships. The mediator class knows about the text field and the button. The text field knows about the mediator only. And the button knows about the mediator only.

When the user types something into the text field, each key press that results in the text changing will cause the notifyMediator method to be called. This method then calls into a changed method defined in the mediator to be called. In order for the mediator to know what actually changed, a common design involves each control sending a pointer or reference to itself to the mediator. At this point, the text field has informed the mediator that something has changed and let the mediator know that it was the source of the change.

Let’s switch vantage points now to the mediator. It’s waiting patiently for something to happen when it’s notified that a change has taken place. The mediator looks at the source of the change and sees that it came from the text field. The mediator also knows that whenever the text field contains text, then the Ok button should be enabled. So the mediator makes a request back to the text field to get whatever text it currently contains.

Let’s switch to the text field for a moment. It’s already informed the mediator that something has changed when it gets a call asking for the current text. The text field really has no knowledge of why somebody wants the text only that some other code is asking for the text. So it responds to the method call by providing its text. It’s possible that there is no text. That’s okay. The text field can just respond with an empty string if it’s empty.

Back to the mediator now that it has the contents of the text field. This is where it puts its special knowledge to work and checks the length of the string. If the string is empty, then the mediator disables the Ok button. And if the string has text in it, then the mediator enables the Ok button.

When the scenario gets more complicated, the mediator may need to check several different classes before doing whatever it needs to do. The main thing is that all of this is wrapped up in one class that’s specific to your relationship scenarios while each of the classes remains simple and reusable.