Prana sample: Movie Application

ActionScript, Design Patterns, Prana No Comments »

I just added a new example application to the Prana SVN Repository. The sample application lets you search for movies based on the name of a director you specify. It is based on the MovieLister example that Martin Fowler describes in his “Inversion of Control Containers and the Dependency Injection pattern” article. I figured this would be a far simpler example than the modified Cairngorm Store.

Read the rest of this entry »


Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 

Prana is now on SourceForge

ActionScript, Design Patterns, Flash, Flex, Prana 3 Comments »

The Prana Framework I mentioned last week (here and here), has now been added to SourceForge.

In short, Prana is an Inversion of Control (IoC) Container written in ActionScript 3. It is a non-intrusive API for configuring Flash/Flex/Apollo applications. The library also contains general utilities. Possible additions may include an AOP framework and Mock Objects support for FlexUnit.

Check it out at http://sourceforge.net/projects/prana/.

A user mailinglist is available at https://lists.sourceforge.net/lists/listinfo/prana-user

Developers interested in sharing ideas or joining in for development can contact me at info{at}herrodius.com.


Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 

Prana: configuring Cairngorm’s FrontController

ActionScript, Design Patterns, Flex, Prana 7 Comments »

Yesterday I introduced the Prana IoC framework for AS3 I'm working on. The example showed how to create an external configuration file that defines the remote services used in the sample application and configures the ServiceLocator. Today I wanted to go a step further and write a config section for the FrontController.

I introduced the CairngormFrontController class that extends Cairngorm's FrontController and lets you pass in commands to the constructor. It then calls the "addCommand()" method on every command found in the constructor argument which results in a configured FrontController instance.

The following object definition was added to the applicationContext.xml file:

XML:
  1. <object id="shopController" class="be.indiegroup.prana.ioc.util.CairngormFrontController">
  2.   <constructor-arg>
  3.     <object>
  4.       <property name="getProducts" value="GetProductsCommand"/>
  5.       <property name="addProductToShoppingCart" value="AddProductToShoppingCartCommand"/>
  6.       <property name="deleteProductFromShoppingCart" value="DeleteProductFromShoppingCartCommand"/>
  7.       <property name="filterProducts" value="FilterProductsCommand"/>
  8.       <property name="sortProducts" value="SortProductsCommand"/>
  9.       <property name="validateOrder" value="ValidateOrderCommand"/>
  10.       <property name="validateCreditCard" value="ValidateCreditCardCommand"/>      
  11.       <property name="completePurchase" value="CompletePurchaseCommand"/>
  12.     </object>
  13.   </constructor-arg>
  14.   <constructor-arg value="com.adobe.cairngorm.samples.store.command"/>
  15. </object>

The first argument is the object that contains the mapping between the event names and the command classes. The second argument is optional and defines the package where the command classes reside. By specifying this argument, you don't need to define the fully qualified classnames of the commands.

We can now leave out the ShopController class, but beware: we need to make sure that the command classes get compiled in the swf of our application. We can do this by referencing them in our application. We'll also reference the CairngormFrontController class:

Actionscript:
  1. private var _cairngormFrontController:CairngormFrontController;
  2. private var _commands:Array = [GetProductsCommand, AddProductToShoppingCartCommand, DeleteProductFromShoppingCartCommand, FilterProductsCommand, SortProductsCommand, ValidateOrderCommand, ValidateCreditCardCommand, CompletePurchaseCommand];

We can also comment out the reference to the ShopController in the main application file:

XML:
  1. <!-- the FrontController, containing Commands specific to this appliation -->
  2. <!--<control:ShopController id="controller" />-->

We are now able to ignore certain view events by leaving out the mapping from the shopController definition. In this application it probably doesn't make much sense, but occasions may arise where this functionality is wanted.

Download Prana 0.1.1

  • Prana Framework 0.1.1
  • Prana Framework 0.1.1 with dependencies
  • Cairngorm Store Sample
  • Download Prana on SourceForge: Prana SourceForge page

Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 

Introducing Prana: IoC Container for AS3

ActionScript, Design Patterns, Flash, Flex, Prana 7 Comments »

Note before starting: For a primer on Inversion of Control (IoC) and Dependency Injection, please read "Inversion of Control Containers and the Dependency Injection pattern" by Martin Fowler.

Prana's IoC container: an example

As an example, I took the Cairngorm Store modified by Renaun Erickson and used Prana to configure its service locator. You could imagine this application being deployed on several clients and they would all have their own AMFPHP gateway. It would be a pain if the application would have to be recompiled for each client just because the path to the remoting gateway changed. With Prana, we can specify the actual implementation of the remote services and the service locator in an external xml file called the application context (applicationContext.xml) and the IoC container will be responsible for setting things up for us. Let's see some code.

The service locator is now configured in the Services.mxml file. One of the services, the 'productService', is configured like this:

XML:
  1. <renaun:RemoteObjectAMF0
  2.   endpoint="http://localhost/amfphp/gateway.php"
  3.   id="productService"
  4.   source="com.adobe.cairngorm.samples.store.business.ProductDelegate"
  5.   showBusyCursor="true"
  6.   makeObjectsBindable="true">
  7. </renaun:RemoteObjectAMF0>

You can see that the endpoint property is pointing to a hard coded remoting gateway. As said earlier, in order to change this value for another deployed client, we would have to change the value and recomile the application. With Prana, we will just leave out the actual implementation like this:

XML:
  1. <renaun:RemoteObjectAMF0 id="productService"/>

So when the application is compiled, the service locator will know about a remote service called 'productService' but it will not know anything about its implementation details.

Defining the application context

We define the external configuration of the productService as follows. This configuration is stored in an xml file, called applicationContext.xml by convention (but you can call it whatever you like):

XML:
  1. <object id="productService" class="com.renaun.rpc.RemoteObjectAMF0">
  2.   <property name="id" value="productService"/>
  3.   <property name="endpoint" value="http://localhost/amfphp/gateway.php"/>
  4.   <property name="source" value="com.adobe.cairngorm.samples.store.business.ProductDelegate"/>
  5.   <property name="showBusyCursor" value="true"/>
  6.   <property name="makeObjectsBindable" value="true"/>
  7. </object>

Every object is defined in an 'object' node. We need to give each object and identifier (the 'id' attribute) and a class name (the 'class' attribute) so that the container knows which class to instantiate. The properties of the productService are defined in 'property' nodes which all have a 'name' and 'value' attribute. You can see for instance that the 'endpoint' property will be assigned the value 'http://localhost/amfphp/gateway.php'.

Note: Prana currently supports setter injection through implicit setters and not through explicit setters. (set myPropery vs. setMyProperty)

Besides the productService, we also have to configure the service locator so that it knows it is assigned a new instance of a remote service:

XML:
  1. <object id="serviceLocator" class="com.adobe.cairngorm.business.ServiceLocator" factory-method="getInstance">
  2.   <property name="productService">
  3.     <ref>productService</ref>
  4.   </property>
  5. </object>

Notice that we have specified a 'factory-method' attribute on the definition of the service locator. This is needed because Cairngorm's ServiceLocator is a singleton which you can access through the getInstance() method. When the container finds a factory method defined, it will not invoke the constructor of the class but instead will call the specified factory method to retrieve an/the instance of the class.

The productService is defined as a property of the serviceLocator, but notice here that the property node does not have an actual value assigned. Instead, is it has a 'ref' node which describes a reference to the id of another object defined in the application context.

Note: Currently reference properties only work when the refered to object is configured before the object that is refering to it. This will be solved in later versions of Prana.

The application context is ready. All we need to do now, is let the application know that it should load the context before starting up. In the Cairngorm Store example the application starts by getting the products for the store. This is defined in the onCreationComplete handler:

Actionscript:
  1. CairngormEventDispatcher.getInstance().dispatchEvent( new CairngormEvent( GetProductsEvent.EVENT_GET_PRODUCTS ) );

We will remove this here line and instead load the application context. Once the context is loaded, we can dispatch the event to get the products:

Actionscript:
  1. private var _objectDefinitionsLoader:IObjectDefinitionsLoader;
  2.  
  3. private function onCreationComplete():void {
  4.   _objectDefinitionsLoader = new XmlObjectDefinitionsLoader();
  5.   _objectDefinitionsLoader.addEventListener(ObjectDefinitionsLoaderEvent.COMPLETE, onObjectDefinitionsLoaderComplete);
  6.   _objectDefinitionsLoader.load("../applicationContext.xml");
  7. }
  8.  
  9. private function onObjectDefinitionsLoaderComplete(event:ObjectDefinitionsLoaderEvent):void {
  10.   CairngormEventDispatcher.getInstance().dispatchEvent( new CairngormEvent( GetProductsEvent.EVENT_GET_PRODUCTS ) );
  11. }

That's it. By loading the application context, all objects will be wired together behind the scenes. If wanted, we could access the container and request the productService from it:

Actionscript:
  1. var container:ObjectContainer = _objectDefinitionsLoader.container;
  2. var productService:RemoteObjectAMF0 = container.getObject("productService");

Other Prana capabilities

The Dependency Injection method in the example above is Setter Injection. Prana however also supports Constructor Injection. An example object definition for a constructor defined as Person(name:String = "", age:int = 0, isMarried:Boolean = false) is:

XML:
  1. <object id="johnDoe" class="be.indiegroup.prana.ioc.testclasses.Person">
  2.   <constructor-arg value="John Doe"/>
  3.   <constructor-arg value="36"/>
  4.   <constructor-arg value="true"/>
  5. </object>

Another thing is that you can (recursively) define properties as arrays or objects like this:

XML:
  1. <object id="testObject" class="be.indiegroup.prana.ioc.testclasses.TestObject">
  2.   <property name="anArray">
  3.     <array>
  4.       <value>stringValue</value>
  5.       <value>13</value>
  6.       <value>true</value>
  7.       <array>
  8.         <value>12</value>
  9.         <value>test</value>
  10.       </array>
  11.     </array>
  12.   <property>
  13.   <property name="anObject">
  14.     <object>
  15.       <property name="key1" value="value1"/>
  16.       <property name="key2" value="35"/>
  17.       <property name="key3" value="false"/>
  18.       <property name="key4">
  19.         <array>
  20.           <value>12</value>
  21.           <value>test</value>
  22.         </array>
  23.       </property>
  24.     </object>
  25.   </property>
  26. </object>

Download Prana 0.1

I hope you enjoyed this primer on Prana. Below is the Prana Framework available for download. It has the Prana.swc file, sources and docs. Please note that documentation is far from complete at this moment. The other download is the Cairngorm Store, modified by Renaun Erickson to use AMFPHP and modified by myself to use Prana. You can download them here:

  • Prana Framework 0.1
  • Prana Framework 0.1.1
  • Prana Framework 0.1.1 with dependencies
  • Cairngorm Store Prana Example
  • Download Prana on SourceForge: Prana SourceForge page

Have fun!


Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 

Typesafe ENUM’s

Design Patterns, Flash 4 Comments »

I was discussing the use of typesafe ENUM's with a co-worker today when we got to a point where we couldn't agree on how to implement some of the behavior. We have this AssessmentItemType class that holds different types of assessmentitems like MULTIPLE_CHOICE, FILL_IN_TEXT, DRAG_AND_DROP_WITH_TEXT, etc that looks like this (some of the content is stripped).

Actionscript:
  1. ...
  2.  
  3. private static var TYPES:Array = new Array();
  4.  
  5. private var id:Number;
  6. private var name:String;
  7.  
  8.  
  9. public static var MULTIPLE_CHOICE:AssessmentItemType = new AssessmentItemType("multipleChoice", 1);
  10. public static var FILL_IN_TEXT:AssessmentItemType = new AssessmentItemType("fillInText", 2);
  11. public static var DRAG_AND_DROP_WITH_TEXT:AssessmentItemType = new AssessmentItemType("dragAndDropWithText", 6);
  12. public static var DRAG_AND_DROP_WITH_IMAGES:AssessmentItemType = new AssessmentItemType("dragAndDropWithImages", 7);
  13.  
  14.  
  15. private function AssessmentItemType(name:String, id:Number){
  16.   setName(name);
  17.   setId(id);
  18.  
  19.   TYPES.push(this);
  20. }
  21.  
  22. ...

These types are assigned to an instance of the AssessmentItem class when we are parsing the assessmentitems from XML files. But during the parsing, we need a way to look up the correct assessmentitemtype in the ENUM. All we know about the type of the assessmentitem is its name, "multipleChoice" for instance, or its id, "1" for instance. The id is used for backward compatibility with the previous system.

The first solution was to create a separate helper class to look up the assessmentitemtype by looking into the available types, exposing the TYPES array. We figured we needed a getByName() and a getById() method in order to look up the type both ways. The second solution was to add these 2 methods to the AssessmentItemType class itself and not to create a separate class. I personally thought that the second solution was the best because creating a separate class to look up values would be overkill in my opinion. We were also showing some the class' internals by making the TYPES array public in order to access it from the helper class. We couldn't end the discussion because my co-worker had to catch a train, so I was wondering what you guys were thinking.

PS: This might not be a critical design question, but both solutions might have there pros and cons where we didn't think of.


Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 
WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Login