It is rare you will find a Flex application that doesn't require data persistence of some sort, whether it be to server via remote objects or web services; or saving locally to an SQLite database as in the case of AIR.
Working on various projects throughout my Flex life, I've come across many different approaches to how this is done, especially in the context of Cairngorm. Exhibit A:
[as3]
// Create my user
var myUser : User = new User( "John", "Smith" );
// Dispatch a Cairngorm event to save it
new UserEvent( UserEvent.SAVE, myUser ).dispatch();
[/as3]
This is the simplest method but, should your event design change, or you want to pass in another argument, you have potentially a lot of code to change where that event may be dispatched. Also there is the added complication that this code could be just about anywhere.
Exhibit B:
[as3]
var myUser : User = new User( "John", "Smith" );
myUser.save();
[/as3]
Now, this is just the same as the above, except the model itself dispatches the Cairngorm Event to save itself. It's nicely encapsulated and I'm happy enough with this approach. However, if you want your command to tell the relevant view (or presentation model) that your user saved successfully or not, that code becomes a little more fussy. For example:
[as3]
package
{
import flash.events.EventDispatcher;
[Event( name="saveSuccess" )]
[Event( name="saveFault" )]
[Bindable]
public class User extends EventDispatcher
{
public static const SAVE_SUCCESS : String = "saveSuccess";
public static const SAVE_FAULT : String = "saveFault";
public function User( forename : String = null, surname : String = null )
{
this.forename = forename;
this.surname = surname;
}
public var forename : String;
public var surname : String;
public function save() : void
{
new UserEvent( UserEvent.SAVE, this ).dispatch();
}
public function saveSuccess() : void
{
dispatchEvent( new Event( SAVE_SUCCESS ) );
}
public function saveFault() : void
{
dispatchEvent( new Event( SAVE_FAULT ) );
}
}
}
[/as3]
The saveSuccess() and saveFault() callbacks, although convenient, clutter your nice clean model code and we start here to lose sight of the core principles of OO design; one class, one single responsibility. After all, our domain model is for storing data, nothing more than that.
I concocted a "pattern" the other day when trying to tackle this problem, and I can't work out if it's genius or the ugliest duckling: The Persister pattern. Yes, I know.
First, we'll create a simple persister interface with a method to save our user, and two methods to report back success or failure.
[as3]
package
{
import flash.events.IEventDispatcher;
public interface IPersister extends IEventDispatcher
{
function save() : void
function saveSuccess() : void
function saveFault() : void
}
}
[/as3]
Now, an even simpler interface for our model (User class) to implement, just to get our persistable object.
[as3]
package
{
public interface IPersistable
{
function getPersister() : IPersister
}
}
[/as3]
And here is our basic implementation:
[as3]
package
{
[Bindable]
public class User implements IPersistable
{
public function User( forename : String = null, surname : String = null )
{
this.forename = forename;
this.surname = surname;
}
public var forename : String;
public var surname : String;
private var persister : IPersister;
public function getPersister() : IPersister
{
if ( !persister )
persister = new UserPersister( this );
return persister;
}
}
}
import flash.events.EventDispatcher;
class UserPersister extends EventDispatcher implements IPersister
{
public function UserPersister( user : User )
{
this.user = user;
}
private var user : User;
public function save() : void
{
new UserEvent( UserEvent.SAVE, user ).dispatch();
}
public function saveSuccess() : void
{
dispatchEvent( new UserEvent( UserEvent.SAVE_SUCCESS, user ) );
}
public function saveFault() : void
{
dispatchEvent( new UserEvent( UserEvent.SAVE_FAULT, user ) );
}
}
[/as3]
Now, you will have noticed that the "persister" is implemented as a private class. This allows us to keep our User object clean, whilst still maintaining that relationship between the model and its persister; nothing else need know about the persister's implementation.
So, nice or ugly? I'm open to thoughts, here's throwing it open to the floor...
No comments:
Post a Comment