I like Cairngorm. I know some folks see it and say "whoa...that's a lot of classes for a single gesture. Events, Commands, Controllers, Delegates, Locators...aieee." However, "everything in its place" mentality and separation of concerns it enforces really appeal to me.
However, one thing I absolutely can't stand is the ViewLocator/ViewHelper bit. The design forces causing it (separation of controller from knowledge of currently existing views and their implementation) are natural, and require a solution. I'm just not sure ViewHelper/ViewLocator are the right answers. They leave me writing code that still knows about the ID strings with which ViewHelpers have registered themselves.
Let's wrap a use case around this: I have a UserLoginEvent mapped to UserLoginCommand. When the UserLoginCommand gets its response from the backend server, I've got some view behavior that I'd like to take place that goes beyond what binding to the ModelLocator can provide. In other words, I need to call a function named loginFailed() on the view. Using ViewHelper/ViewLocator, I can add a ViewHelper to the view that exposes a loginFailed() function that the command will call: its purpose is to call the actual loginFailed() function on the view, disconnected the view's implementation from the command/controller layer. Complicated.
I started looking through the core Cairngorm code last night, and I noticed that the CairngormEventDispatcher's addEventListener() message just wants two parameters: the name of the event listened for and a reference to a listener function.
In Model-Glue terms:
In Cairngorm, as in Model-Glue, the function referenced is normally on a controller.
However, unlike Model-Glue (and HTML apps in general), Flex/Flash gives us the ability to have views respond and change their display without reloading the page. This means there's no technical reason why the listener function couldn't be on a view.
By adding functions acting as intialized() and removed() handlers, you can add code to your view that listens for events, just like the controller:
CairngormEventDispatcher.getInstance().addEventListener(AuthenticationController.USER_LOGIN_RESULT, loginResults);
}
private function removeViewListeners():void {
CairngormEventDispatcher.getInstance().removeEventListener(AuthenticationController.USER_LOGIN_RESULT, loginResults);
}
Now, through a sequence of events, I can have the UserLoginEvent's handling code fire a UserLoginResult event. When it's dispatched, my view will "hear" it and fire its loginResults function. Simple.
Conclusion
The CairngormEventDispatcher allows for functions that aren't just methods on the controller to be added as event listeners. By taking advantage of this, we can allow views to respond to architectural events as well as controllers.
It's not without problems: our view is suddenly bound to Cairngorm's event architecture. I'm also not sure how much this stretches MVC...the model's still completely separate, which is my largest concern. It's just that the views are taking on some knowledge of the controller tier (names of events, existence of certain controllers used to get those names, the dispatcher itself) that I'm not sure they should have.
Thoughts?
Comment 1 written by mike on 7 February 2007, at 9:19 AM
Comment 2 written by Spike on 7 February 2007, at 11:50 AM
I created a an event (CalBackEvent) that extends CaringormEvent and adds an optional callback function. Each command has a property called completionListener typed as Function. In command.execute I check if the event is of type CallBackEvent. If so, completionListener is assigned. When command.result() is fired, completionListener is called. In order to provide a framework to give some meaningful information to the code inside the completion listener I added a ViewAdvisor layer that inspects the result from the server, and possibly the model, and creates an instance of a ViewAdvice class. This class acts as a Transfer Object for information that I want to pass to the view.
Within the view layer, responsibility for view control goes from parent to child. Each parent is completely responsible for the lifecycle of its children.
So far it's working out pretty well.
Comment 3 written by bokel on 7 February 2007, at 12:41 PM
Comment 4 written by Spike on 7 February 2007, at 12:46 PM
Comment 5 written by Dave Carabetta on 7 February 2007, at 2:32 PM
Regarding your concerns about coupling, I'm on the fence. If I'm building truly reusable widgets for my overall application, I don't use Cairngorm so that I can easily drop it in to any application. I like to keep those framework-agnostic for obvious reasons. However, the coupling of Cairngorm is not just limited to your views...your events, commands, delegates, etc., all do as well. I don't mind this because Cairngorm brings along with it the separation you talked about and other great features that keep my codebase structured, organized, and "clean." Yes, my views "know" about Cairngorm, but so does the rest of my application.
Just my $.02
Comment 6 written by Jay Proulx on 8 February 2007, at 12:14 PM
Dispatch a CairngormEvent, your ViewController is listening, and switches states. Verrrry simple. You can also have your FrontController and ViewController listening to the same events so that logic runs at the same time as changing views, also very handy.
Since a ViewController is typically implemented as an MXML component, you can create as many of them as you need for your application. You can nest them, and use them any time you want a CairngormEvent to represent a change in views.
So far, I haven't found a downside to it, it's completely encapsulated and abstracted, and there is _NO_ logic/view co-mingling.
I've already contacted the cairngorm-devel guys and I'm trying to get it added to the core cairngorm release.
Comment 7 written by Phil on 11 August 2007, at 6:57 PM
Comment 8 written by ???? on 6 January 2008, at 10:28 PM
<a href="http://www.hzmq.net/">http://www.hzmq.net&...;
<a href="http://www.hzmqzs.com/">http://www.hzmqzs....;
<a href="http://www.0571ax.com">http://www.0571ax.c...;
<a href="http://www.byzxw.com">http://www.byzxw.com...;
Comment 9 written by Maverick on 14 March 2008, at 8:25 AM
Now coming to solve the problem you narrated, I agree that using "events" makes sense as that also falls in the "Flex-programming paradigm". But, how do we allow users to add events is the key.
One of the most important part is to find a way that is seam-less. I am not sure, if you have seen the "Command-Responder" pattern. I would like to do something like that. I would have seen Cairngorm implement that with the View and Command; where the view passes an object to the Command and then in the command you add to the view to the responder. Which means once when the Command will raise the event it will automatically get caught - but till it does not comes, using the approach you outlined is great.
Comment 10 written by Mark Campion on 1 April 2008, at 5:56 AM
[Add Comment] [Subscribe to Comments]