Oct 10, 2009

Flex – Multi View Design Pattern

Problem

  • Need to develop a flex web application which is going to be deployed in different environment say in mobile and web.
  • Need to come up with different views for different devices and for different resolution. But the base view logic (event handling and state) should be same.

Solution

Follow the flex multi-view design pattern.

Multi - View Design Pattern

Multi-View design pattern uses passive design pattern and factory design pattern.

clip_image002

Passive View Design Pattern

The Passive View pattern is a derivative of the Model View Presenter (MVP), which is considered a derivative of the Model-view-controller. The Passive View pattern has some similarities to the “Code Behind” implementation, since both achieve a complete separation of Action Script logic and MXML or AS component tags. The passive presentation model allows us to easy test our application since we can create test cases just for the logic and if needed test cases for the view.

In passive view pattern we split our laundry to two piles, view and presenter:

View

  • View is passive and is not aware of the Presenter.

Presenter

  • Logic is in the Presenter.
  • Presenter observes view events.
  • Presenter updates the view data.
  • Presenter ‘knows’ about the components in the view.
  • Presenter holds the data or point to a data class.

By moving all the logic out of the view, the Passive View pattern can achieve the separation of designer and developer work and create a paradigm where it’s easy to change the view. The view class contains only the components and their states, no events, logic, changes or model.

Factory Design Pattern

Now throw in the mix the factory design pattern and you can actually create few views for each presenter.
The factory pattern is one of the most basic creational patterns that deal with the problem of creating different products without specifying the exact concrete class that will be created. This is done by creating a separate method for creating the product abstract class, whose subclasses can override to specify the derived type of product that will be created.

Sample Application

This sample application provides more clarity on multiple view design pattern. Basic view is to provide more granularities on design issues. Below are the screen snapshots.

  • The application will have multiple views of same screen.
  • ‘Submit’ button on both the screens will handle the same business logic at one point. There is no duplicate code to handle the same functionality in each screen.

Screen 1: To select different resolution.

clip_image004

Screen 2: User selects 400 X 300 screen size.

clip_image006

Screen 3: User selects 800 X 500 screen size.

clip_image008

Read the below steps for better understanding of the code. To implement multi view design pattern follow the below steps

  • Create an abstract action script view class. This class should extend mx.core.UIComponent. All user defined mxml should extend this class. In our example this class name is AbstractPassiveMainView.as. This abstract class should hold all the components that will be used by presenter.
  • Presenter knows the logic and has all control on view components. In our example ‘submit’ button event handling will be handled using the presenter class (Presenter.as).
  • Factory class is used to create instance for mxml or view classes.
  • Creator class is used to get the mxml object and passes through presenter for business handling.

Source Code Explanation

MultiViewApplication.mxml – This is the main application mxml file. This screen has a drop down which allows user to select different views. On value change loadView() action script method will be called. This loadView() method invokes the Creator object and adds the child to Canvas container.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundColor="#6797CE" backgroundGradientColors="[#ffffff,#6797CE]">
<mx:Script>
<![CDATA[
import mx.core.Container;
import view.Factory;
import view.AbstractPassiveMainView;
import mx.events.FlexEvent;
protected function loadView(event:Event):void
{
containerView.removeAllChildren();
var creator:Creator = new
Creator(event.currentTarget.selectedItem);
containerView.addChild(creator);
}
]]>
</mx:Script>
<mx:ComboBox x="139" y="29" change="loadView(event)">
<mx:ArrayCollection>
<mx:String>select</mx:String>
<mx:String>400 X 300</mx:String>
<mx:String>800 X 500</mx:String>
</mx:ArrayCollection>
</mx:ComboBox>
<mx:Canvas x="10" y="63" id="containerView"/>
<mx:Label x="10" y="31" text="Select Resolution:" fontSize="12"/>
</mx:Application>


Creator.as - This action script class decides which view to be loaded. Here we are getting the view instance using factory design pattern.



package
{
import mx.core.UIComponent;
import view.AbstractPassiveMainView;
import view.Factory;
import view.presenter.Presenter;
public class Creator extends UIComponent
{
private var mainView:AbstractPassiveMainView;
private var presenter:Presenter;
public function Creator(displayMode:String)
{
if(displayMode == "400 X 300")
{
mainView = Factory.createView(Factory.PASSIVE_MAIN_VIEW_1);
this.addChild(mainView);
presenter = new Presenter(mainView);
}
else if(displayMode == "800 X 500")
{
mainView = Factory.createView(Factory.PASSIVE_MAIN_VIEW_2);
this.addChild(mainView);
presenter = new Presenter(mainView);
}
}
}
}


Factory.as - This action script class is used to create view objects.



package view
{
import flash.errors.IllegalOperationError;
import view.mainview1.PassiveMainView1;
import view.mainview2.PassiveMainView2;
public final class Factory
{
public static const PASSIVE_MAIN_VIEW_1:int = 0;
public static const PASSIVE_MAIN_VIEW_2:int = 1;
public static function
createView(type:Number):AbstractPassiveMainView
{
var retVal:AbstractPassiveMainView;
switch (type)
{
case PASSIVE_MAIN_VIEW_1:
retVal = new PassiveMainView1();
break;
case PASSIVE_MAIN_VIEW_2:
retVal = new PassiveMainView2();
break;
throw new IllegalOperationError("The view type "
+ type + " is not recognized.");
}
return retVal;
}
}
}


PassiveMainView1.as – This is a view which includes our registartion1.mxml(400 X 300). I haven’t created this submit button in Registartion1.mxml because this button instance should be part of AbatractPassiveMainView.



package view.mainview1
{
import mx.controls.Button;
import view.AbstractPassiveMainView;
import view.subview.PassiveSubView1;
public class PassiveMainView1 extends AbstractPassiveMainView
{
public var registrationView:RegistrationView1;
public function PassiveMainView1()
{
registrationView = new RegistrationView1();
button = new Button();
button.label = "Submit";
button.x = 206;
button.y = 157;
registrationView.addChild(button);
this.addChild(registrationView);
passiveSubView = new PassiveSubView1();
this.addChild(passiveSubView);
}
}
}




RegistartionView1.mxml – plain mxml.



<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow title="New Registration" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" borderStyle="inset" xmlns:view="view.mainview1.*" width="400" height="300" showCloseButton="true" headerColors="[#000000,#000000]">
<mx:Label x="43" y="57" text="Name"/>
<mx:TextInput x="111" y="53"/>
<mx:Label x="43" y="85" text="Address 1"/>
<mx:TextInput x="111" y="83"/>
<mx:Label x="43" y="113" text="Address 2"/>
<mx:TextInput x="111" y="111"/>
</mx:TitleWindow>


PassiveMainView2.as - This is another view which includes our registartion2.mxml (800 X 500). I haven’t created this submit button in Registration1.mxml because this button instance should be part of AbatractPassiveMainView.



package view.mainview2
{
import mx.controls.Button;
import view.AbstractPassiveMainView;
import view.subview.PassiveSubView1;
public class PassiveMainView2 extends AbstractPassiveMainView
{
public var registrationView2:RegistrationView2;
public function PassiveMainView2()
{
registrationView2 = new RegistrationView2();
button = new Button();
button.label = "Submit";
button.x = 595;
button.y = 239;
button.width = 102;
registrationView2.addChild(button);
this.addChild(registrationView2);
passiveSubView = new PassiveSubView1();
this.addChild(passiveSubView);
}
}
}



RegistrationView2.mxml



<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow title="New Registration" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
borderStyle="inset" xmlns:view="view.mainview1.*" width="800" height="500" showCloseButton="true" headerColors="[#000000,#000000]">
<mx:Label x="43" y="57" text="Name"/>
<mx:TextInput x="43" y="83" width="654"/>
<mx:Label x="43" y="114" text="Address 1"/>
<mx:TextInput x="43" y="134" width="654"/>
<mx:Label x="43" y="164" text="Address 2"/>
<mx:TextInput x="43" y="190" width="654"/>
</mx:TitleWindow>


AbstractPassiveMainView.as - The abstract class holds all the components that will be used by presenter. This button object is nothing but the submit button that I have used in the above 2 mxml.



package view
{
import mx.controls.Button;
import mx.core.UIComponent;
public class AbstractPassiveMainView extends UIComponent
{
public var button:Button;
public var passiveSubView:AbstractPassiveSubView;
public function AbstractPassiveMainView()
{
}
}
}


AbstractPassiveSubView.as



package view
{
import mx.controls.Button;
import mx.core.UIComponent;
public class AbstractPassiveMainView extends UIComponent
{
public var button:Button;
public var passiveSubView:AbstractPassiveSubView;
public function AbstractPassiveMainView()
{
}
}
}


PassiveSubView1.as – This is the output view. Here on click of submit button I‘m displaying a message ‘Data Added Successfully’.



package view.subview
{
import mx.controls.Text;
import view.AbstractPassiveSubView;
public class PassiveSubView1 extends AbstractPassiveSubView
{
public function PassiveSubView1()
{
text = new Text();
text.y = 60;
text.x = 50;
text.width = 250;
text.height = 20;
text.setStyle("color", "0xfa0303");
text.text = "Data Added Successfully.";
text.visible = false;
this.addChild(text);
}
}
}


Presenter.as - The presenter handles the logic of the main application, as well as using sub presenter and sub view if needed. Here I have added the ‘Submit’ button event. Logic is in the presenter. Presenter ‘knows’ about the components in the view.



package view.presenter
{
import flash.events.MouseEvent;
import view.AbstractPassiveMainView;
public class Presenter
{
// Corresponding view
private var passiveMainView:AbstractPassiveMainView;
// Sub-presenters
private var subPresenter:SubPresenter;
public function
Presenter(passiveMainView:AbstractPassiveMainView)
{
this.passiveMainView = passiveMainView;
subPresenter = new
SubPresenter(passiveMainView.passiveSubView);
passiveMainView.button.addEventListener(
MouseEvent.CLICK, onButtonClick);
}
private function onButtonClick(event:MouseEvent):void
{
subPresenter.setTextLabelVisible(true);
}
}
}


SubPresenter.as 



package view.presenter
{
import view.AbstractPassiveSubView;
public class SubPresenter
{
private var passiveMainSubView:AbstractPassiveSubView;
public function
SubPresenter(passiveMainSubView:AbstractPassiveSubView)
{
this.passiveMainSubView = passiveMainSubView;
}
public function setTextLabelVisible(isVisible:Boolean):void
{
passiveMainSubView.text.visible = isVisible;
}
}
}


References





0 comments:

Text Widget

Copyright © Vinay's Blog | Powered by Blogger

Design by | Blogger Theme by