Mediator Pattern
Introduction
Developers in .NET sometimes come from scripting language environments that are not strong on Object Oriented Methodologies. OO methodologies like refactoring and using design patterns can be intimidating and the value for the developer is hard to see. What developers new to the OO world, and even more seasoned developers need to understand is that good design is not beyond scope in any project. Good habits simply must be learned that will make the developer a better designer.
In that mind set, I am submitting the first of several "how to" real
world examples, used in my professional life, on how to use patterns in a simple,
easy to follow manner. Many of us have ended up either writing or working on
in-line, scripting based code. Taking some ideologies from refactoring methodology,
this is not necessarily a bad thing. Of course, when you are first writing an
algorithm or a series of logic statements, it seems easier to start with if....then....else
.
But as you continue to expand this code to more complex forms, this model can
quickly become unmanageable. This is where refactoring and design pattern methodology
can play an important and useful part in simplifying, enhancing and making code
more useable (and understandable) to those who practice good OO design.
This article details a real world example of how to use the mediator pattern to help you develop a method to communicate between several classes. The mediator pattern is used when mediation is needed between several classes, to communicate in a indirect manner.
BackGround
I have used mediators in several different ways throughout my professional
career, but the cleanest way to recognize a mediator pattern is any class that
facilitates indirect communication between a series of other classes. This example
is fairly light in inheritance, simply because I did not want to add too much
into the conversion between if...then...else
code and the pattern,
thus potentially confusing the developer on the minimum requirements of the
mediator pattern.
How to use the code
The first thing we should note is we see the WorkerObject
again
in this example, but it is slightly different. It contains this time a constructor
that accepts a string parameter, a two way accessor and a method SendMessage
.
public class WorkerObject { private string _message; public WorkerObject(string message) { _message = message; } public string Message { get{return _message;} set{_message = value; } public SendMessage(string message) { Console.WriteLine("Message sent : " + message); } }
Next we see the functional code, as it exists at the beginning of the refactoring
effort. Notice the class construction declarations, each passing in a slightly
different string variable, and behind that, the if...then...else
statement, which checks the Message
accessor of the sender object
and based on that value not being the same as the sender object, performs the
SendMessage
method on the appropriate object.
WorkerObject senderObject = new WorkerObject("message0"); WorkerObject workerObject1 = new WorkerObject("message1"); WorkerObject workerObject2 = new WorkerObject("message2"); WorkerObject workerObject3 = new WorkerObject("message3"); if(!workerObject1.Message.Equals(senderObject.Message) { workerObject1.SendMessage(senderObject.Message); } if(!workerObject2.Message.Equals(senderObject.Message) { workerObject2.SendMessage(senderObject.Message); } if(!workerObject3.Message.Equals(senderObject.Message) { workerObject3.SendMessage(senderObject.Message); }
Now to move this code from the overly cumbersomeif...then...else
statements, we create a mediator class, DoSomeMediation
. This class
contains two methods Register
and SendMessage
. The
Register
method catalogs all the classes we want to mediate between.
The SendMessage
method is where the functional code actually exists,
and calls all the registered objects, ignoring the passed in object if it exists
in the mediator class, and calls the SendMessage
method on each
class, passing in the passed objects Message
accessor.
public class DoSomeMediation { private static ArrayList _workerObjects = new ArrayList(); public static int Register(WorkerObject workerObject) { return _workerObjects.Add(workerObject); } public static void SendMessage(WorkerObject senderObject) { if(senderObject == null) return; string messageToSend = senderObject.Message; foreach(WorkerObject workerObject in _workerObjects) { //send message to all other objects registered if(!workerObject.Message.Equals(senderObject.Message)) workerObject.SendMessage(messageToSend); } } }
Next we see the execution code. Notice that while we still construct the WorkerObject
objects the same way, now instead of the if...then...else
code
making the determination of which objects to call the SendMessage
method on, we let the mediator class handle the communication between the classes.
WorkerObject senderObject = new WorkerObject("message0"); WorkerObject workerObject1 = new WorkerObject("message1"); WorkerObject workerObject2 = new WorkerObject("message2"); WorkerObject workerObject3 = new WorkerObject("message3"); DoSomeMediation.Register(senderObject); DoSomeMediation.Register(workerObject1); DoSomeMediation.Register(workerObject2); DoSomeMediation.Register(workerObject3); DoSomeMediation.SendMessage(senderObject);
Points of Interest
This particular example seems to just be redoing the if...then...else
statement in a different manner. What does this buy us? Well lets say we wanted
to pass into the executed code a collection of WorkerObject
objects,
instead of the manual construction. Using the if...then...else
code we could not make a runtime change to the number or type of objects that
could communicate between each other.
This is the third installment in the series I am writing on real world design patterns. All examples and the bulk of this article are taken from my professional experience as an architect. The examples given are templates only, and the designer must keep in mind that they are the ones who must decide where different patterns, if any, may be best used in their code.
Deciding to perform a refactoring effort from existing code to a pattern must be weighed on the necessity and need of the code itself. Patterns are only design templates, helpers to accommodate better overall design. I might stress that making the effort to use patterns will strengthen your overall design ability, but like your basic coding skills, it is something learned and cultivated.
If this or any other in this series on design patterns is helpful or you have questions or comments please e-mail me at chris.lasater@gmail.com.
History
This is the first revision and is the third installment in a series.
Related Articles
Other articles in this series include: