Keeping views and mediators nicely decoupled when using pureMVC can sometimes be a bit tricky. Declaring and using a concrete view within its corresponding mediator often seems the easiest way to accomplish things; but lands you with a very tightly coupled pair. Even though the view probably has no knowledge of the mediator it is the view that is most likely to change, meaning the mediator also requires modification.
Object oriented design advises against using concrete implementations for these very reasons. This article goes through a few ideas of ways to put this into practise.
Using the event model for two way communication.
Events are probably the most common way of achieving view to mediator communication. A mediator that knows its view component implements IEventDispatcher
can add listeners and handle events dispatched by the component. Custom event classes can be used to pass data around.
This can work the other way round as well. A mediator that knows its view component implements IEventDispatcher
can dispatch events on the view component for the component to handle itself. Custom events can be used once again to pass data from the mediator to the view.
The mediator now only needs to type its view component to IEventDispatcher
or some high level implementation of (such as DisplayObject
or UIComponent
). This way views and mediators can fully communicate their intentions and the data that goes with them without any explicit knowledge of their concrete implementations.
Downsides to this: the code becomes a little harder to follow and you may need to add custom event classes (and the accompanying boilerplate) to pass the necessary data around.
Another similar approach would be to use a different messaging system instead. The Actionscript event model is perhaps a bit over complicated for a straightforward two way communication like the one described above.
Something along the lines of AS3 Signals could be used to create a lighter weight means of mediator-view communication in the same mould.
Using multiple interfaces.
Instead of a mediator having one view component reference - capable of handling all of the responsibilities inherent in their relationship - it could have several. Even if these references all point to the same object this is a way of dividing up the responsibilities the mediator has to the view into simpler, less strongly tied parts.
A mediator could be supplied an IEventDispatcher
for event handling, a DisplayObject
for positioning and several other interfaces/abstract classes to handle other aspects of the view.
This allows the view to be changed around without touching the mediator. Certain aspects could also be made to be optional in the mediator. For example supply an optional text component reference. If it is set then set its text, otherwise leave it. It also makes extending the mediator easier. To add on another responsibility is as easy as extending the mediator and adding another reference to another interface or class.
On the downside this also adds complexity and more interfaces. It also begs the question "how far do you break things down?". Do you need an IEventDispatcher
for events when you've got a DisplayObjectContainer
for adding and removing children? Ultimately it will depend whether the first is ever going to be needed without the second. Probably not in this case but the more complex the example the more likely it would be worth doing.
Another issue with this approach is when there is lack of common interfaces defined. A Flex Button
has a label
property, and a Label
has a text
property. Both accomplish the same thing, but as there's no common interface for setting their text this method is of no use.
Let the view decide.
Instead of making updating the responsibility of the mediator, make it the responsibility of the view. Then view components only have to implement an updating interface to achieve mediator to view communication.
When a notification is received by the mediator it's body is just passed straight into a call to the view's update method. The view can then check its content (for example check its type) before actioning the necessary update.
The biggest downside to this approach is the loss of compile time type checking. While using value objects can give good runtime checking, it adds processing overhead and invites errors that may not be found for some time.
Finally...
I will add some examples and any more thoughts I have on this subject soon.
UPDATE: A brief Flex example. http://db.tt/PRXGzvaX