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: final proxy classes (a question about style)  (Read 8953 times)
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« on: December 21, 2009, 07:21:22 »

I'd like to get some opinions about how people define their proxy classes, specifically regarding the constructor and the class definition.

I have a situation (which I think is common) where my proxy basically defines its own name by default and (since it gets its data remotely) also doesn't need the data parameter in its constructor.  Furthermore, the proxy is a single instance proxy...  This led me to write this constructor:

:
public function MyProxy() { //argument-less constructor
  super(NAME, null);
}

But then I thought, this class can't even be extended so why not just make it final?

:
public final class MyProxy extends Proxy {
  ...
}

AND THEN, I thought, this class can only have one instance so why not make it a singleton?

And there I stopped.  Because AS 3.0 has gimpy singleton support, as a rule, I never implement singletons.  Even though my data models are often de-facto singletons.  Again, I just don't like singleton pattern in AS 3.0 because Adobe doesn't encourage it and by extension the language doesn't properly support it.

So, then I thought, if I'm going to be anal about all this, then what does pureMVC say about final classes, especially final Proxies?

And what does pureMVC say about precluding extension by leaving out the proxyName and proxyData parameters in the constructor?

The whole point is to make the code easy for a new programmer to grasp and give them a sensible precedent for adding their own classes.  To that end, what is the best practice here?  How far should I go?  Even to the extreme of making my proxy a singleton?  I realize this may mostly be a style decision, but I want to make my own style decision here an informed one.  So please, if you have an opinion, share it with me.  Thanks.
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #1 on: December 21, 2009, 08:42:23 »

Maybe I should expound a little more about what is at stake here... and ask for your opinion in a slightly different way.

I want to make the code legible and easy to understand not just inside the Proxy classes but, and more importantly, outside.  So, I have 3 ways so far of writing the same thing (assume the following takes place in a Command):

1) verbose:

:
if (!facade.hasProxy(MyProxy.NAME)) {
  facade.registerProxy(new MyProxy(MyProxy.NAME)); //data is set later
}
var myProxy:IProxy = facade.retrieveProxy(MyProxy.NAME);

2) parameter-less constructor (and maybe final class???):
:
if (!facade.hasProxy(MyProxy.NAME)) {
  facade.registerProxy(new MyProxy());
}
var myProxy:IProxy = facade.retrieveProxy(MyProxy.NAME);

3) singleton:
:
var myProxy:IProxy = MyProxy.getInstance();
My goal is to have something easy to understand that that discourages bad logic.
But I also know that this may really boil down to style, and that's fine.  Like I said, I want to make an informed opinion regarding style at the very least.  Likewise, if there are other recommendations, I'd love to see them!
Logged
adrianw
Newbie
*
Posts: 8


View Profile Email
« Reply #2 on: December 22, 2009, 03:44:25 »

My proxies have only one parameter - the data, because i've never had an occasion to give my proxies names at runtime (they just are public static const). So I would choose gate nr 2.

I don't know if final keyword would help in increasing performance of application, but if you're looking for places for performance improvement, you could try.

Proxies as singletons would result in mess in my opinion. Testing PureMVC application isn't easy and making proxies singletons would make this process much harder. You already have Facade singleton which is used to retrieve proxies. Singleton proxies would also remove the possibility to remove and register them at runtime.
Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #3 on: December 23, 2009, 06:25:50 »

Thanks for the feedback.  The final keyword isn't about performance so much.  In fact, I'd be surprised if it had any effect on performance.  Mostly, it would just be to make crystal clear to someone else that, hey, this class cannot and should not be extended.

One reason I could leave out the data parameter entirely is that my proxies all retrieve their own data from a remote server.  I'm putting that logic in their constructor or a retrieveData() method.  I guess I should have emphasized that above...
Logged
adrianw
Newbie
*
Posts: 8


View Profile Email
« Reply #4 on: December 24, 2009, 02:11:55 »

No problem, it's only my humble opinion ;)

Sometimes, the data parameter in the constructor of proxies helps me when I want to configure them from external XML file. In that file I have for example endpoint address to my webservice. I parse that file with ConfigProxy (which is the first proxy registered in my app) and when config file is parsed and ConfigVO is ready to use, I register and configure the remote service proxies at once, passing ConfigVO into the constructor of proxy.
When you're deploying your application to the destination server, the endpoints of services will mostly be different than those from development server, so you don't have to recompile your app with new endpoint address, you just change them in config file.

Thats advantage of using data parameter in constructor.
I think there could be a number of situations where the data parameter would be helpful.

I don't know if you're using multicore or singlecore version, but i think better place for initialization or other things done when proxy/mediator is registered, is onRegister(). In multicore doing too much in constructor of proxy/mediator is a source of errors like "Multiton key not yet initialized...", so I learned to use onRegister() rather than constructor.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: December 27, 2009, 10:49:56 »

this class can't even be extended so why not just make it final?
That's not always true. I often create an AbstractProxy in my application that extends Proxy and adds things like a service object and methods for standard fault handling and sending error and logging notifications. Then I have concrete proxies extend the AbstractProxy instead of Proxy. Or, if I'm doing an AIR application, I may have several XML files that I'd like to be able to read, write and manipulate, so I subclass the XMLDatabaseProxy (in the XMLDatabase utility) which completely abstracts the business of creating, retrieving and saving the file disk.

I have a situation (which I think is common) where my proxy basically defines its own name by default and (since it gets its data remotely) also doesn't need the data parameter in its constructor.
Yes, this is common. How you handle the data parameter is up to the individual Proxy and the patterns of its use within your system.

There are two typical kinds of Proxies with respect to the data object. Those that hold one data object and those that hold a collection of objects.

In the former case, your calling super with null for the data object (actually, you can just omit the parameter, it's optional) will usually be fine. It all has to do with whether this Proxy is likely to be retrieved and have its data property accessed before it has been set. It might be that you want a blank VO to be there instead.

In the latter case, a collection, I usually pass a new instance of the collection to the superclass constructor.

:
public function MyProxy() { // still an argument-less constructor
  super(NAME, new Array()); // make sure that methods that manipulate the collection won't bomb
}

Also, in your verbose example of constructing a Proxy,
facade.registerProxy(new MyProxy(MyProxy.NAME)); //data is set later
You don't want to pass MyProxy its name which is defined on the class itself. Just pass nothing and let MyProxy tell the Proxy superclass its own NAME.

This leads into the singleton discussion.
I just don't like singleton pattern in AS 3.0 because Adobe doesn't encourage it and by extension the language doesn't properly support it.
Cairngorm has singletons, and Adobe certainly advocates its use. There is a lot of FUD (fear, uncertainy and doubt) about singletons to be sure. The 'singletons are useful / evil' horse has been beaten to a pulp here and elsewhere on the interwebs, so I won't get into it other than to say that used improperly, they can certainly do more harm than good, but that they do have a place. Just not as Proxies.

If you implement that Proxy as a Singleton, there's nothing to prevent you from just snagging the instance in your view component as a quick and easy way of getting at the data. That might seem expedient, but it ties the view component to the framework, an anti-pattern.

With regard to the Proxy; yes most Proxies end up being singletons albeit in use not implementation. There is no need to create them according to the singleton pattern. Only one Proxy can be registered with the Model for any given name. By defining MyProxy.NAME, and passing that to super in the constructor of MyProxy, the system cannot have more than one registered MyProxy. Retrieving it by name gives you that one instance.

However all Proxies aren't necessarily singletons in use. For instance, you may have an app that manages your employees and you can have as many of them as you'd like open in different tabs. Often if there is a lot of complex data behind something like an account, you may grab it a piece at a time as needed instead of all at once.

In the EmployeeAdmin demo, we have EmployeeProxy that has a collection of EmployeeVOs, but in a more complex version of the application that drills deeper into the Employee's details, retrieving various related objects, you might have an EmployeeDetailProxy for every EmployeeVO. The EmployeeProxy might get a a list of EmployeeVOs at startup, which populates a list, but when you select that employee and ask for details, you register an EmployeeDetailProxy for that employee, passing the EmployeeVO on the constructor, where you'd use the id property as the name to register the proxy by and the VO as the data object. In the EmployeeDetailProxy.onRegister method, you'd make a service call to get the details for this employee. The proxy has the master and detail data for the employee all in one place. So it's not a singleton.

:
public function EmployeeDetailProxy( employeeVO:EmployeeVO ) {
  super(employeeVO.id, employeeVO);
}

This situation is nice because it means the EmployeeProxy doesn't have to have logic to deal with the list as well as individual objects. All creating, updating and deleting of individual Employees would go through the EmployeeDetailProxy.

The nice thing about the Proxy Pattern as implemented within PureMVC is there is one way of getting at any Proxy whether it is to be used as a singleton or not, and that way insulates the view components from tying themselves to the framework and thus reducing their portability.

-=Cliff>
« Last Edit: December 27, 2009, 11:14:12 by puremvc » Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #6 on: January 06, 2010, 09:19:13 »

I think this is what I need to do but still need some help on how.  I have an application that manages projects.  Upon logging in my ProjectProxy goes and gets all of the data from the project_tbl and sends it to the MainScreen Mediator. 

1.  The MainScreen has a projectList array collection that holds all of the project data. The MainScreen view's initial state has a ADG that shows only some of the project data.  When the user clicks on an item the state changes, sliding up the ADG and showing a TabNavigator with 4 tabs that shows the project detail.  The first tab is General Info showing all of the data for the project stored in the MainScreen's projectList array. The MainScreen also has a currentProject VO that holds the currently selected project.  Is it ok to store this data in the view?  When the user edits a project I am going back to the database to get the updated project data.  I feel this may not be the most efficient way.

2. How should I manage getting the data for the other tabs ie. Resource Tab, Financial Info Tab, Comments Tab?  The ADG is still shown to the user.  When the user clicks on a different project I need to go and get the data for these other tabs.  Should I have a ProjectDetailsProxy that holds a ResourceTabVO, FinancialTabVO, CommentsTabVO?  Or should each of these tabs have its own proxy?  Should I have a proxy for the ADG which shows the summary data?

Thanks for listening.
Logged
bestbuyernc
Full Member
***
Posts: 21


View Profile Email
« Reply #7 on: January 08, 2010, 01:49:16 »

Ok. So here's what I have done.  I tried to follow what Cliff said.  Please hang with me.

I have created a ProjectSummaryVO that will just hold the project summary data used to list summary project data on the MainScreen's datagrid after successfull login. The ProjectSummaryProxy is responsible for getting the project summary data when the application starts up.
My MainScreen view has an instance of the ProjectSummaryVO named currentProject that is populated each time the user clicks a project item on the grid.  I have a custom ProjectEvent that is dispatched sending along the currentProject.

:
// MainScreenView.as
[Bindable] public var currentProject:ProjectSummaryVO = new ProjectSummaryVO;

// happens when user selects a project item on the data grid
public function selectProjectHandler(event:ListEvent):void {

// populate currentProject from selected item
currentProject = event.itemRenderer.data as ProjectSummaryVO;

// go get this project's details
this.dispatchEvent(new ProjectEvent(ProjectEvent.GET_PROJECT_DETAIL, currentProject));
}

The ProjectEvent is heard by the MainScreenMediator.  The MainScreenMediator then calls the facade to register a unique ProjectDetailProxy using the projectID passed via the event.

:

// MainScreen Mediator
override public function onRegister():void {
appProxy = facade.retrieveProxy(ApplicationProxy.NAME) as ApplicationProxy;
loginProxy = facade.retrieveProxy(LoginProxy.NAME) as LoginProxy;
projectProxy = facade.retrieveProxy(ProjectProxy.NAME) as ProjectProxy;

// listen for main screen events
mainScreen.addEventListener(LogoutEvent.LOGOUT, tryLogout);
mainScreen.addEventListener(ProjectEvent.GET_PROJECT_DETAIL, getProjectDetail);
}

// create a unique proxy for this project's details
public function getProjectDetail(event:ProjectEvent):void {
facade.registerProxy(new ProjectDetailsProxy(event.project);
}

Like Cliff said, when I register the new ProjectDetailsProxy I make a service call to get the project details.

:
// ProjectDetailsProxy //

// remote object instance references
private var project:ProjectDetailVO = new ProjectDetailVO();

// constructor takes project
public function ProjectDetailsProxy(currentProject:ProjectSummaryVO) {
project.projectID = currentProject.projectID;
super(currentProject.projectID.toString(), project);
}     


// configure the proxy when registered
public override function onRegister():void {
        projectService  = new RemoteObject("ProjectService");
        projectService.source = "TSProjectRunwayServiceLibrary.com.tspr.business.ProjectService";
        projectService.getOperation("getProjectDetails").addEventListener(ResultEvent.RESULT, projectDetailsLoaded);
        projectService.getOperation("getProjectDetails").addEventListener(FaultEvent.FAULT, projectDetailsFailed);
         
        // immediately get the project details           
       getProjectDetails(this.projectVO.projectID);
}

Ok. So now a new ProjectDetailsProxy is created each time the user clicks on a different project.  My question is now. Am I overwriting an existing ProjectDetailsProxy when I click one project, click another project and then click back on the previous one?  Since the same projectID is being used as the ID.  Or should I be asking the facade if a ProjectDetailsProxy exists with this projectID before creating another one?  If the facade still has a reference to the proxy then that would keep me from having to go back to the database for the same project details, However, how many proxys is too much? When should I start deleting them?

Thanks for your patience.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #8 on: January 11, 2010, 09:09:43 »

This all looks good, and the only thing that stuck out as I was reading it ended up being the subject of your final question:

Am I overwriting an existing ProjectDetailsProxy when I click one project, click another project and then click back on the previous one?
Yes, you are. You do need to check if that proxy is there first.

:
// create a unique proxy for this project's details
public function getProjectDetail(event:ProjectEvent):void
{
        if ( ! facade.hasProxy( ProjectSummaryVO( event.project ).projectID )  {     
                facade.registerProxy( new ProjectDetailsProxy( event.project ) );
        }
}

Of course this begs the question, what happens when the user selects a project in the grid and this method gets fired, but the details have already been viewed once.

You currently make the service call in onRegister of the ProjectDetailsProxy. This means it'll only be fired once. Not only does that foul up this particular logic, but it is generally not a good idea to call services immediately on Proxy registration anyway. The reason is that since we usually prep the Model first and then the View at startup,  we can't be sure that the return from the service won't happen BEFORE the interested parties in the View are registered. So usually we just prep the Model without making calls, then we prep the View. Then we initiate service calls by invoking some Proxy methods.

A better way to do this would be to refactor ProjectDetailsProxy like this:

:
// configure the proxy when registered
public override function onRegister():void
{
        projectService  = new RemoteObject("ProjectService");
        projectService.source = "TSProjectRunwayServiceLibrary.com.tspr.business.ProjectService";
        projectService.getOperation("getProjectDetails").addEventListener(ResultEvent.RESULT, projectDetailsLoaded);
        projectService.getOperation("getProjectDetails").addEventListener(FaultEvent.FAULT, projectDetailsFailed);
}

public var loaded:Boolean=false; // set true by projectDetailsLoaded       
public function getProjectDetails( id:string ):void
{
        if (loaded) { //previously fetched
                // send same note that projectDetailsLoaded sends with cached details
                sendNotification(PROJECT_DETAILS_LOADED, data);

        } else {      // fetch now
                projectService.getProjectDetails(id);
        }
}

And then revisit your MainScreenMediator's getProjectDetail event handler

:
// create a unique proxy for this project's details
public function getProjectDetail(event:ProjectEvent):void
{
        var projectID:String = ProjectSummaryVO( event.project ).projectID
        var detailsProxy:ProjectDetailsProxy = facade.retrieveProxy( projectID );
        if ( detailsProxy == null )
                detailsProxy = new ProjectDetailsProxy( event.project );
                facade.registerProxy( detailsProxy  );
        }
        detailsProxy.getProjectDetails(projectID);
}

This should get you to a place that works, but the problem is that all these proxies have their own service instances. A more optimum way would be to eventually refactor the ProjectDetailsProxy to mind a collection of ProjectDetails objects, so that it could just be registered once and have one service instance.

You could alleviate some of the memory problems with this by unhooking the service event listeners and nulling the service out when your results are retrieved (unless they need to write as well). This would mean the proxies floating around don't have services after they've used them once. But you still incur the object creation time for the service with each new proxy created.

-=Cliff>
« Last Edit: January 11, 2010, 09:13:39 by puremvc » Logged
jpwrunyan
Sr. Member
****
Posts: 84


View Profile WWW Email
« Reply #9 on: January 11, 2010, 11:37:20 »

Cairngorm has singletons, and Adobe certainly advocates its use. There is a lot of FUD (fear, uncertainy and doubt) about singletons to be sure. The 'singletons are useful / evil' horse has been beaten to a pulp here and elsewhere on the interwebs, so I won't get into it other than to say that used improperly, they can certainly do more harm than good, but that they do have a place. Just not as Proxies.

If you implement that Proxy as a Singleton, there's nothing to prevent you from just snagging the instance in your view component as a quick and easy way of getting at the data. That might seem expedient, but it ties the view component to the framework, an anti-pattern.

Thanks, you articulated my general "feeling" on this particular instance of using the Singleton pattern.  One of my peers is making the proxies into Singletons but until now I didn't feel like I had a sound argument to present to him against doing that (without taking a no-singletons-at-all-ever hard line stance).

I'll follow your other advice as well and try to make it the standard in our team!
Logged
Pages: [1]
Print