Saturday, 27 December 2008

Flex MVC frameworks

Over the last two months, I have been involved in writing Flex code using the Cairngorm framework. It claims to be an MVC framework. On closer examination, Cairngorm provides an alternative event mechanism that focuses on the use of commands, a standard location for the implementation of services, and a set of development principles.

The Cairngorm framework encourages the use of singletons for:

  1. A front controller that is used to register commands and events that fire them,
  2. A service locator used to hold dictionaries to remote object, http service, or web services. The service delegates use this to locate the correct service object,
  3. A model locator used to gain access to the applications data model,
  4. A view locator that allows the location of views from commands if required, and
  5. An event dispatcher that dispatches events using Flash event mechanism.

The event dispatcher is behind the scenes as the programmer doesn't need to be aware of its existence. It is the only one of these singletons that is actually referenced by any of the Cairngorm code. None of the others actually need to be singletons.

The Cairngorm framework also recommends the defining of event classes for each event in the application. The primary purpose of the event class / object is to define the data to be passed to the command executed when the event is fired. An ICommand interface is provided to give a standard command type. A series of events can be chained together by defining commands that extend the sequence command class.

The view locator and view helper are provided so that reference back to view objects can be achieved if required in the application. A value object interface is provided for model value objects.

The basic processing structure us that user gestures are translated into Cairngorm events which are dispatched causing the associated command to be executed. The command causes the model to be updated. Through the data binding mechanisms, the view is notified of changes and refreshes.

The first impression when trying to use the Cairngorm framework is that all these singletons restrict the ability to test drive the code or to have multiple contexts within the application. Since none of the singletons are actually required by the framework, it proves easy to use the Cairngorm event mechanisms and the basic structure but not use all the singletons.

In my experimentation, in order to be able to test code using remote objects, I have mocked out the service locator and the associated remote objects. A little bit of exploring of the Flex SDK on the definition of remote objects showed that this is relatively easy to do. We use a application controller singleton to hold references to the front controller (register of events and commands), service controller, and the model. A façade class defines static functions that make it easier to use our code structure.

I have implemented an initialisation entry point for the application controller that allows me to define alternative front controllers, service controllers, and models. I have also implemented a method to destroy the singleton so it can be reset for a second set of tests.

With this structure, it is possible to inject test code to verify the correct firing of events, to trap service calls to verify that they are being set up correctly. So far, I haven't replaced model elements but that could be achieved as well. More importantly, through mocking out the services, it is possible to fire test data back into the application to verify its overall operation.

Instead of using the view helper and view locator, we have implemented our own notifier. This is not too different from Cairngorm events except that it doesn't restrict the number of receivers of the notification and the receiver can say what method to call when the notification is sent. With out notifier, we could discard the Cairngorm framework completely but we already had a commitment to its use.

Sticking rigidly to the Cairngorm conventions introduces a lot of redundant code. For example coding an event class for every event or the delegate structure for accessing services. We have eliminated the use of event classes but at this stage are still using delegates. Some code refactoring may see those removed.

For the views, we have defined a mediator actionScript class that extends the base class used for the view that it relates to. The mxml view file then uses this as its base tag thus from the perspective of the mxml making the actionScript class appear to be part of the mxml. The advantage of this is that we implement most of the code that carries out actions for the view in the mediator class. We can then test drive this free of the mxml components. I have endeavoured to enforce that the only way that the mxml view component can be updated is though the data binding mechanisms. In a sense, the mediator is a supervising controller (Fowler 2006).

We have also examined the PureMVC framework and concluded that like Cairngorm, the advantage of PureMVC is the conventions that it proposes rather than the specific code that it implements.
PureMVC suggests the use of:

  1. Views with associated mediators that sit between view and rest of system (our naming comes from here although I originally called them presenters),
  2. A controller and commands with both a simple command and a macro command (similar to Cairngorm's sequence command),
  3. A model accessed through proxy objects (The proxies provides access to remote data through services. Domain logic implemented in proxies),
  4. A façade that acts as the gateway to the application (Brokers requests to the model, view, and controller. Initialises the controller with command mappings. Uses commands to prepare model and view), and
  5. Observers and Notifications (These provide a publish, subscribe mechanism. Notifications trigger commands. Mediators send, declare interest in and receive notifications. Proxies send but do not receive notifications).

Again most of this is convention rather than requirement. The only real code in the PureMVC framework is in the observers and notification structure. The rest is primarily interfaces so that conventions are being followed.

The idea of both frameworks is to reduce the binding between views, the business logic, and the model. Cairngorm has chosen to use an event structure that restricts the events to firing one command. PureMVC has chosen to implement a more flexible notification structure. Both frameworks are quite small. The core components (the event or notification mechanism) can be implemented quite quickly by any experienced programmer.

My conclusion is that you should think carefully about what you want to achieve and the overhead introduced by using these frameworks. In my case, my objective was implementing the idea of a supervising controller and endeavouring to ensure the minimum amount of dependency in the code. I have already decided that I wouldn't use either Cairngorm or PureMVC on future projects unless mandated by the client. A simple notification mechanism and some careful coding conventions will serve my purposes.

Reference

Fowler, M. (2006, 19 June). Supervising controller. Retrieved 9 November, 2008, from http://martinfowler.com/eaaDev/SupervisingPresenter.html

1 comment:

Dascalita D said...

The supervising presenter is what I was analyzing a while ago.
Coming from Cairngorm, moving to pureMVC 1.5 yrs ago I also concluded that Mediators should act as Presenters and they should create a minimal Presentation Model to handle to the view: the Presentation Model contains just the data to display, and the View send events that the Presenter listens and responds to. The advantage is that you can use the data binding facility without memory leaks. When you remove the view and its Presenter/Mediator, the Presentation Model which was created by the Presenter can be disposed by the garbage collector.
The second advantage I see is the increased test coverage. With a Presenter you need to mock up your view. The Presentation Model doesn't constrain you to do so. So when creating tests, you take the view into consideration.