|
Design Patterns in RIAThis article talks about a number of design patterns that can be implemented in the client-tier to enable the building of robust, scalable, and maintainable RIAs using techniques and patterns that will be familiar to J2EE developers. This document assumes that users are familiar with Adobe Flex.
RIA Model-View-Controller (MVC) PatternAdobe Flex is based on a Model-View-Controller (MVC) architecture. Flex primarily focuses on the view part of the MVC architecture. It also provides some controller logic to help communicate with remote systems. Although the Flex application can be considered part of the View in a distributed MVC (Model 2 MVC), it also implements its own MVC architecture at the client side. A Flex application has its own view components (typically written in MXML), model components (representing data at the client side), and controller components (responsible for the communication with back-end systems). RIA Front Controller PatternThe Front Controller implementation can be achieved using ActionScript 2.0 with a collaboration of two key classes that work in an event-driven manner.
In Flex, components can have events attributed to them. Consider the example of a simple login screen. An event can be specified to the Login button like below:
<mx:Button label="Login" click="EventBroadcaster.getInstance().broadcastEvent( ICommand.LOGIN_COMMAND )" />
The click event is generated when the user clicks the button. On the click event EventBroadcaster is invoked to broadcast the event as:
EventBroadcaster.getInstance().broadcastEvent(Command.LOGIN_COMMAND )
The argument to the broadcastEvent method is a String which is the event name. The role of Front Controller is to first register all the different events that it is capable of handling against worker classes, called Command classes. On hearing an application event, the Front Controller will look up its table of registered events, find the appropriate command for handling of the event, before dispatching control to the Command by calling its execute() method. In the Login application that is being discussed,
import com.inkriti.framework.flex.control.*;
import com.inkriti.flex.demo.commands.*; class com.inkriti.flex.demo.control.DemoController extends FrontController { public function DemoController() { addCommand( ICommand.LOGIN_COMMAND, new LoginCommand() ) } } This is the code for the DemoController that is included in the Login application MXML file in the previous section. All the events in the application are registered by adding Commands. For more information on Command pattern look at the next section. RIA Command PatternBy using the Command Pattern to manage the "work" associated with each request, a scalable architecture can be achieved in a simple fashion. As the complexity of the application increases, the number of use-cases also typically increases - each use-case largely corresponds to the creation of new command class and the registering of the event name with the Controller, which should cause control to pass to this command class. Lets look into the LoginCommand class for more details.
import com.inkriti.framework.flex.business.Responder;
class com.inkriti.flex.demo.commands.LoginCommand implements Command, Responder
//-------------------------------------------------------------------------
public function execute( event:Event ) : Void
var loginVO = new LoginVO();
loginDelegate.login( loginVO );
//-------------------------------------------------------------------------
public function onResult( event : Object ) : Void
var userListVO : UserListVO = event.result;
//-------------------------------------------------------------------------
public function onFault( event : Object ) : Void
//-------------------------------------------------------------------------
private var loginDelegate: LoginDelegate;
The LoginCommand class implements Command class and the main method to be seen is the execute() method. In the execute() method the values of the login user name and password is extracted from LoginViewHelper. ViewHelper classes encapsulate all the elements in the MXML file. In the application discussed, a view is declared in Login.mxml as:
<demoView:LoginViewHelper name="{IView.LOGIN_VIEW}" view="{ this }" />
So the LoginViewHelper class has access to the elements in Login.mxml file. import com.inkriti.framework.flex.view.ViewHelper;
class com.inkriti.flex.demo.view.LoginViewHelper extends ViewHelper
//-------------------------------------------------------------------------
public function getUsername() : String
//-------------------------------------------------------------------------
public function getPassword() : String
//-------------------------------------------------------------------------
public function loginFailed() : Void
Going back to the LoginCommand classes's execute() method, first get the instance of LoginViewHelper and get the values of the form elements in Login view and populate a Value Object LoginVO (for more on Value Objects see next section). Finally it calls the delegate to do the necessary business logic. LoginCommand also implements a Responder which has to methods: onResult() and onFault().
var demoViewHelper = ViewLocator.getInstance().getViewHelper( IView.DEMO_VIEW );
The resulting ViewHelper in our example is the DemoViewHelper which shows list of user in the system. This view was registered in the main applications MXML file. Then the resulting VO is referenced as:
var userListVO : UserListVO = event.result;
Finally the VO is passed to the DemoViewHelper view as:
demoViewHelper.switchToMainView( userListVO );
Similarly in onFault method the same procedure is followed. In the application discussed, the same login screen is shown with a failed message. So first get the view as
var loginViewHelper = ViewLocator.getInstance().getViewHelper( IView.LOGIN_VIEW );
Finally set the custom message shown on the screen as:
loginViewHelper.loginFailed();
The LoginViewHelper has a method loginFailed() as:
public function loginFailed() : Void
Since LoginViewHelper encapsulates all the elements defined in Login.mxml it has access to statusMessage variable defined in Login.mxml (see the previous section).
RIA Business Delegate PatternThe Business Delegate represents a clear point of integration between the client and the server. Business Delegate typically fulfills its role in collaboration with the Service Locator; It uses Service Locator to locate and look up remote services, such as web services, remote Java Objects or HTTP services. Once located, Business Delegate invokes these services on behalf of the class that has delegated responsibility to it for business logic invocation.As seen in the previous section, Command class is responsible for deciding which business services should be invoked and ensures that the appropriate data is collected to call these services. The Service Locator class looks up for the service requested by the name of the service. ServiceLocator class maintains an array of services. In the application discussed, ServiceLocator is subclassed and services defined using the following code:
<?xml version="1.0" encoding="utf-8"?>
<mx:RemoteObject id="loginDelegate" source="com.inkriti.flex.demo.business.LoginDelegate"
</inkriti:ServiceLocator> Lets looks into the delegate class. Delegate class has methods that implement the business logic for a particular use-case. In the application discussed, the LoginDelegate has login method which calls the login method defined in the underlying Java layer.
import com.inkriti.framework.flex.business.*;
class com.inkriti.flex.demo.business.LoginDelegate
//------------------------------------------------------------
public function login( loginVO : LoginVO ): Void
call.resultHandler = Delegate.create( Object( responder ), responder.onResult );
//-------------------------------------------------------------
private var responder:Responder;
In the constructor, it gets the instance of the underlying service by means of Service Locator (discussed earlier). ServiceLocator class has access to var call = service.login( loginVO ); After executing the remote service methods, the resultHandler and the faultHandler are set as discussed earlier:
call.resultHandler = Delegate.create( Object( responder ), responder.onResult ); Delegate is a standard ActionScript 2.0 class shipped with Flex which allows developers to create a function wrapper and run the function. In the application discussed, a wrapper is created for Responder Object which is passed through the constructor of the LoginDelegate and call the appropriate function.
RIA Value ObjectsData passed between various layers in the enterprise application is encapsulated in the form of Value Objects. Value Objects in Java layer are basic java beans which have set and get methods for each private member of the bean. Value Objects allow to do the following:
SummaryThis article explained in brief the design patterns for RIA development that are likely to be familiar to J2EE developers. This article would have given the reader a good understanding of how to develop RIAs with Adobe Flex and learned how ActionScript 2.0 and MXML can work together to enable the development of RIAs.
References
|