Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Is there any wrong with this implementation?  (Read 9030 times)
xmrcivicboix
Newbie
*
Posts: 6


View Profile Email
« on: March 05, 2008, 01:48:57 »

I wanted to know if there is anything wrong about this implementation of mine. Bascially, I have a GetPhotosCommand which implements a simple IResponder interface containing a fault and result handler (similar to Cairngorm). So instead of using a Proxy to get remote data, I created a GalleryDelegate which does that then use the Proxy to save data and send out notifications.

Command
:
public class GetPhotosCommand extends BaseCommand
{
private var gallery:GalleryProxy;

override public function execute(note:INotification):void
{
var delegate:GalleryDelegate = new GalleryDelegate(this);
delegate.getAllImages();
}

override public function result(data:Object):void
{
var rs:Array = data.result as Array;
var images:Array = new Array();
for (var i:int = 0; i < rs.length; i++)
{
var image:ImageVO = GalleryUtils.getImageVO(rs[i]) as ImageVO;
images.push(image);
}

gallery = facade.retrieveProxy(GalleryProxy.NAME) as GalleryProxy;
gallery.addImages(images);
}

override public function fault(data:Object):void
{

}
}


Delegate
:
public class GalleryDelegate
{
public static const GATEWAY:String = "http://localhost/thaihuynh/amf/gateway/index.php";

private var service:ServiceProxy;
private var responder:IResponder;

public function GalleryDelegate(responder:IResponder)
{
this.responder = responder;

service = new ServiceProxy(GATEWAY, "gallery");
service.addEventListener(ResultEvent.RESULT, resultHandler);
service.addEventListener(FaultEvent.FAULT, faultHandler);
service.addEventListener(FaultEvent.CONNECTION_ERROR, faultHandler);
}

public function getAllImages():void
{
service.call("getImages", []);
}

private function resultHandler(event:ResultEvent):void
{
responder.result(event);
}

private function faultHandler(event:FaultEvent):void
{
responder.fault(event);
}
}


Proxy
:
public class GalleryProxy extends Proxy implements IProxy
{
public static const NAME:String = "RemoteProxy";

private var images:Array = [];

public function GalleryProxy(data:Object=null)
{
super(NAME, data);
}

public function addImages(images:Array):void
{
this.images = images;
sendNotification(ApplicationFacade.GALLERY_LOADED, images);
}

public function getImages():Array
{
return images;
}
}
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: March 05, 2008, 03:02:44 »

Excellent. This is the perfect setup to discuss why this work is done in the Proxy and not the Command in PureMVC.

There are a couple of reasons:

1) From the PureMVC perspective, Commands should be stateless. By making a running Command a responder you have given it state. It is in a waiting state. If it has a long timeout and the user clicks a 'stop' button, how do you interrupt it? You can't. You can have another Command tell the delegate to cancel the HTTPService call, but does that free the reference to this command, which was on the responder list for the call?

You have handed a reference to a Command to another framework that you don't control. You have no way to ensure that that reference will eventually resolve to a result or fault callback (and that the framework has cleaned up its reference before returning) that will close scope and make the Command go away. Also, remember this pattern might be implemented on a J2ME or some other platform and assumptions about the other framework may not hold.

Meanwhile the Proxy is registered and the framework has a method for removing it. It can only remove a mapping for a Command. This is why we only have the Controller create and execute Commands and we avoid giving them state so they get their work done and go away, and we control their scope.

2) This is Domain Logic not Business Logic. There is a distinction between the two. Business logic is concerned with coordinating valid interactions between View and Model. Domain Logic is about keeping an internally consistent Model representation on both sides of the app (client and server).

The Proxy's job is to hide the location of the data, and make it irrelevant to the rest of the application. The View and Controller regions need only know that a particular proxy wants to be communicated with in a synchronous fashion or async.

This is why services have been entirely relegated to the Model region. You can write a more portable Model this way.

Several applications with different use cases and View/Controller arrangements may employ the same Model classes without having to reimplement or copy the code for keeping the Model internally consistent.

In order to make your proposed classes more compliant with PureMVC best practices, you could simply:

 * Have the Command pass the reference to the Proxy instead of itself to the Delegate.
 * Refactor/move the result and fault methods from the Command to the Proxy

QED.

-=Cliff>
Logged
xmrcivicboix
Newbie
*
Posts: 6


View Profile Email
« Reply #2 on: March 05, 2008, 07:44:58 »

After a little refactoring first round of refactoring, I have the following:

Command
:
public class GetPhotosCommand extends SimpleCommand implements ICommand
{
override public function execute(note:INotification):void
{
var service:GalleryProxy = facade.retrieveProxy(GalleryProxy.NAME) as GalleryProxy;
service.getGalleryImages();
}
}

Proxy
:
public class GalleryProxy extends Proxy implements IProxy
{
public static const GATEWAY:String = "http://localhost/thaihuynh/amf/gateway/index.php";
public static const NAME:String = "RemoteProxy";

private var service:ServiceProxy;
private var images:Array = [];

public function GalleryProxy(data:Object=null)
{
super(NAME, data);

service = new ServiceProxy(GATEWAY, "gallery");
service.addEventListener(ResultEvent.RESULT, resultHandler);
service.addEventListener(FaultEvent.FAULT, faultHandler);
service.addEventListener(FaultEvent.CONNECTION_ERROR, faultHandler);
}

private function resultHandler(event:ResultEvent):void
{
var results:Array = event.result as Array;
for (var i:int = 0; i < results.length; i++)
{
var image:ImageVO = GalleryUtils.getImageVO(results[i]) as ImageVO;
images.push(image);
}
sendNotification(ApplicationFacade.GALLERY_LOADED, images);
}

private function faultHandler(event:FaultEvent):void
{
trace(event.fault);
}

public function getGalleryImages():void
{
service.call("getImages", []);
}

public function getImages():Array
{
return images;
}

I'm still a bit skeptical about this because down the road, I'm going to add a getAlbums method and I want to reuse the GalleryProxy. Does that mean I have to create another proxy since I only have 1 result handler? I mean I guess I can pass in the command reference to the proxy then in my result handler, I can have a factory class to create my value object. Please advise.

:
private function resultHandler(event:ResultEvent):void
{
var results:Array = event.result as Array;
for (var i:int = 0; i < results.length; i++)
{
var image:IValueObject = factoryClass.createValueObject(results[i], commandRef);
images.push(image);
}
sendNotification(ApplicationFacade.GALLERY_LOADED, images);
}
Logged
xmrcivicboix
Newbie
*
Posts: 6


View Profile Email
« Reply #3 on: March 05, 2008, 08:07:32 »

Another look at it, I guess I can create a delegate that will have methods like getAlbums, getImages, etc... then create individual proxy to meet those needs. How does this look?

:
public class GetPhotosCommand extends SimpleCommand implements ICommand
{
override public function execute(note:INotification):void
{
var service:GalleryProxy = facade.retrieveProxy(GalleryProxy.NAME) as GalleryProxy;
service.getGalleryImages();
}
}

:
public class GalleryProxy extends Proxy implements IProxy, IResponder
{
public static const GATEWAY:String = "http://localhost/thaihuynh/amf/gateway/index.php";
public static const NAME:String = "RemoteProxy";

private var delegate:GalleryDelegate;
private var service:ServiceProxy;
private var images:Array = [];

public function GalleryProxy(data:Object=null)
{
super(NAME, data);

delegate = new GalleryDelegate(this);
}

public function result(data:Object):void
{
var results:Array = data.result as Array;
for (var i:int = 0; i < results.length; i++)
{
var image:ImageVO = GalleryUtils.getImageVO(results[i]) as ImageVO;
images.push(image);
}
sendNotification(ApplicationFacade.GALLERY_LOADED, images);
}

public function fault(data:Object):void
{
trace(data.fault);
}

public function getGalleryImages():void
{
delegate.getGalleryImages();
}

public function getImages():Array
{
return images;
}
}

:
public class GalleryDelegate
{
//public static const GATEWAY:String = "http://thaihuynh.com/amf/gateway/index.php";
public static const GATEWAY:String = "http://localhost/thaihuynh/amf/gateway/index.php";

private var responder:IResponder;
private var service:ServiceProxy;

public function GalleryDelegate(responder:IResponder)
{
this.responder = responder;

service = new ServiceProxy(GATEWAY, "gallery");
service.addEventListener(ResultEvent.RESULT, resultHandler);
service.addEventListener(FaultEvent.FAULT, faultHandler);
service.addEventListener(FaultEvent.CONNECTION_ERROR, faultHandler);
}

public function getGalleryImages():void
{
service.call("getImages", []);
}

public function getAlbums():void
{
service.call("getAlbums", []);
}

private function resultHandler(event:ResultEvent):void
{
responder.result(event);
}

private function faultHandler(event:FaultEvent):void
{
responder.fault(event);
}
}
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #4 on: March 05, 2008, 09:14:18 »

With one exception. Don't call it service. The Command doesn't know or care that a service is involved, only that it needs the Proxy to get the gallery images.

The Proxy is hiding the location of the data.

For instance, the data may be cached and the Proxy's getGalleryImages method checks to see if the images are cached and sends a GALLERY_IMAGES_READY Notification. Or it could be that in an offline mode, it creates an array of an internal placeholder asset for each image so the app can continue to function.

In either case no service was invoked. So, to the Command, only the Proxy was involved.

:
public class GetPhotosCommand extends SimpleCommand implements ICommand
{
override public function execute(note:INotification):void
{
var galleryProxy:GalleryProxy = facade.retrieveProxy(GalleryProxy.NAME) as GalleryProxy;
galleryProxy.getGalleryImages();
}
}
« Last Edit: March 05, 2008, 09:16:15 by puremvc » Logged
Pages: [1]
Print