tutorials flex indigo

INDIGO – the architectural framework

Article written 24/01/2007 18:14.
By iteratif (Iteratif ).

As architect, project manager and developer we are often in need of a solid application architecture which can evolve and be maintained. The n-tier architecture answers these needs no matter what the language used even if the language influences the implementation.

An n-tier architecture generally includes a presentation layer, a services and trade objects layer and a data access layer.

Since the start of my .NET developing in 2000 with VS.NET I have used design patterns a lot to implement an n-tier architecture. My applications are solid and can evolve but can be encumbrent to maintain because of anomalies caused by the abusive use of patterns.

In 2004 there came the appearance of ‘Inversion of Control’ (IOC) containers in projects such as Spring, Avalon or Pico which use a simple OOP principal: ‘Don’t call us, we’ll call you’ also called ‘the Hollywood principal’.

We had to wait until August 2004 for Spring (java) in .net with Spring.net. This architectural framework met to my needs, I therefore developed all of my applications by using the basics and principals of OOP and to manage the dependencies between each layer of the Spring.net framework. Since the, my applications have been much easier to maintain.

I will also respond to a question that I had previously asked: How should we use design patterns? And this response is based upon my own experience on the subject, it is totally useless to learn the 23 main patterns by heart, because you can simply deduce OOP basics and principals by using them. I have even experienced, after implementing some code, finding the prescence of a design pattern. But if you don’t live and breathe design patterns you can’t understand their true uses (How to use design pattern - interview of Erich Gamma member of the celebrated Gang Of Four and co-inventor of the JUnit framework).

The power of the IOC container resides in its capacity to resolve dependencies between each layer of your architecture and therefore leave your mind concentrated on the needs of your application. I will share my Indigo framework made in AS3 with you which implements an IOC container inspired by the Spring.net framework using a simple and informative example of a film search.

The Indigo framework proposes an IOC container with the injection of code by accessors (Setter Injection). I suggest that you read the well-known article of the .net community at the following address: http://www.dotnetguru.org/articles/dossiers/ioc/ioc.htm as well as Martin Fowler’s excellent article: http://www.dotnetguru.org/articles/dossiers/ioc/Fowler/IoC.htm on the lightweight containers.

Download the INDIGO framework in beta 1 and examples of this post: INDIGO

Here is the architecture in layers of the 1 MovieFinder example:

If we look closer at the MovieLister class code, we can see that they are dependent on the SimpleMovieFinder class:

public class MovieLister extends EventDispatcher {
      private var _movieFinder:SimpleMovieFinder;
      ...
      public function MovieLister() {
            _movieFinder = new SimpleMovieFinder();
	    _movieFinder.fileName = "movies.txt";
      }
      ...
      public function moviesDirectedBy(director:String):Array {
            searchDirector = director;
	    var allMovies:Array = _movieFinder.findAll();
	    return allMovies.filter(filterMethod);
      }
      ... 
}

So the first thing to do is to not depend on concrete classes but more so on abstractions. We could also use an interface:

public class MovieLister extends EventDispatcher {
      private var _movieFinder:IMovieFinder;
      ...
      public function MovieLister() {
            _movieFinder = new SimpleMovieFinder();
      }
      ...
      public function moviesDirectedBy(director:String):Array {
            searchDirector = director;
	    var allMovies:Array = _movieFinder.findAll();
	    return allMovies.filter(filterMethod);
      }
      ... 
}

But while we remain dependent on the SimpleMovieFinder class, we can still use an object factory which returns an IMovieFinder instance type but we still remain dependent on the factory itself.

Hence the idea of a container which looks after the dependencies between the classes:

To start with we impose a contract on the SimpleMovieFinder class with the help of the IMovieFinder interface:

public class SimpleMovieFinder implements IMovieFinder {
      ...
      public function SimpleMovieFinder() {
 
      }
      ...
      public function allMovies():Array {
            return _movies;
      }
      ... 
}

with IMovieFinder :

public interface IMovieFinder {
      function allMovies():Array;
}

To allow the IOC container to construct the dependency between the two classes we have to define an accessor (Setter) on the MovieLister class which the container will use to create the dependency:

public class MovieLister extends EventDispatcher {
      private var _movieFinder:IMovieFinder;
 
      public function set movieFinder(value:IMovieFinder):void {
            _movieFinder = value;
      }
 
      // this property is unused but we use it
      // to verify that the container has done its job
      public function get movieFinder():IMovieFinder {
	    return _movieFinder
      }
      ...
      public function MovieLister() {
      }
      ...
      public function moviesDirectedBy(director:String):Array {
            searchDirector = director;
	    var allMovies:Array = _movieFinder.findAll();
	    return allMovies.filter(filterMethod);
      }
      ... 
}

We can see that our class is no longer dependent on a concrete class but on an abstraction…

We will now build an IOC container and configure the dependencies with the help of a configuration file.

The configuration file:

<?xml version="1.0"?>
 <objects>
        <object name="myMovieLister" type="examples.movieFinder.MovieLister">
            <property name="movieFinder" ref="myMovieFinder"/>
        </object>
        <object name="myMovieFinder" type="examples.movieFinder.SimpleMovieFinder"/>
</objects>

The tags used are simple:

  • objects is the root of the XML document
  • object shows that new object needs to be added in the container’s referential
  • the attribute name of the object indicates the name of the object at the heart of the container, it’s with this name that we can create an instance of the object
  • the attribute type of the object indicates the class which allows you to instantiate the object
  • the tag property allows you not only to indicate the name of the object accessor to the container but moreover to indicate its dependency with the help of the attribute ref

On the SearchMovie.mxml file we instantiate the XmlObjectFactory class, an IOC container which uses the xml file of the configuration not only knows the objects to instantiate but also their dependencies:

import indigo.objects.factory.xml.XmlObjectFactory;
import indigo.objects.factory.IObjectFactory;
 
private var lister:MovieLister;
 
private function doInit():void {
	// We load the XML configuration file
	var url:URLRequest = new URLRequest("config.xml");
	var loader:URLLoader = new URLLoader();
	loader.addEventListener(Event.COMPLETE,completeHandler);
	loader.load(url);
}
 
private function completeHandler(e:Event):void {
	var loader:URLLoader = e.target as URLLoader
	var root:XML = XML(loader.data);
	// Once the file is loaded we go to the constructor
	// of the XmlObjectFactory class
	var factory:XmlObjectFactory = new XmlObjectFactory(root);
	// The light container loads objects in its referential
	factory.load();
 
	// The getObject() method of XmlObjectFactory allows you to instantiate
	// the corresponding object with the help of the object name as well as its dependencies
	lister = factory.getObject("myMovieLister") as MovieLister;
	Alert.show(lister.movieFinder); // sortie : "[object SimpleMovieFinder]"
}

Obviously you need to indicate to the compiler to include the SimpleMovieFinder class:

And if all goes well it should work!

Let’s continue with the IOC container with a second example where instead of having films written statically in the class, they are loaded from a text file with the help of a class.

Here is the class:

package examples.movieFinder
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
 
	public class ColonDelimitedMovieFinder extends EventDispatcher implements IMovieFinder
	{
		private var _fileName:String;
		private var _movies:Array;
		private var loader:URLLoader;
 
		public function set fileName(value:String):void {
			_fileName = value;
		}
 
		function ColonDelimitedMovieFinder() {
			_movies = [];
		}
 
		public function findAll():Array
		{
			return _movies;
		}
 
		public function load():void {
			if(!loader) {
				loader = new URLLoader();
			}
			loader.addEventListener(Event.COMPLETE,completeHandler);
			loader.load(new URLRequest(_fileName));
		}
 
		private function completeHandler(e:Event):void {
			var loader:URLLoader = e.target as URLLoader;
			loader.removeEventListener(Event.COMPLETE,completeHandler);
 
			_movies = getMovies(loader.data);
 
			dispatchEvent(new Event(Event.COMPLETE));
		}
 
		private function getMovies(src:String):Array {
			var movies:Array = [];
 
			var lines:Array = src.split("\r\n");
			for each(var movie:String in lines) {
				var properties:Array = movie.split(":");
				movies.push({title:properties[0],director:properties[1]});
			}
 
			return movies;
		}
	}
}

Let’s now modify our configuration file by adding a new class:

<?xml version="1.0"?>
 <objects>
        <object name="myMovieLister" type="examples.movieFinder.MovieLister">
            <property name="movieFinder" ref="anotherMovieFinder"/>
        </object>
        <object name="myMovieFinder" type="examples.movieFinder.SimpleMovieFinder"/>
        <object name="anotherMovieFinder" type="examples.movieFinder.ColonDelimitedMovieFinder">
	    <!—As you can see we can also define data other than an object with the help of the attribute value -->
            <property name="fileName" value="movies.txt"/>
        </object>
</objects>

We have seen that the container manages the object dependencies but it can also work with with other types such as strings, numbers etc… Which is what we will do with the file name to load.

As with the previous version you must remember to include the ColonDelimitedMovieFinder class in the compiler arguments. Let’s compile and we should see the following message in the alert window: \[object ColonDelimitedMovieFinder\]

Now to example 3 where this time we have a user interface:

You just need to modify the object reference for the dependency with the myMovieLister object and all without recompiling, by simply modifying the configuration file:

Let’s test it:

Test1

Then let’s change the reference:

Let’s test it: Test2

The power resides in the delegation of dependency between layers to the IOC container which relies solely on an object oriented programming principal leaving you with free reign to concentrate on your application without knowing all of the existent patterns to develop a solid, evolutive and maintainable application.

I will leave you now to judge for yourselves with the code source of INDIGO framework examples that I have provided.

In a future tutorial, I will show you how to divide each layer of your architecture into modules (swf) and to load them with the help of an XML configuration by adding an attribute to the tag object:

<?xml version="1.0"?>
 <objects>
        <object name="myMovieLister" type="examples.movieFinder.MovieLister" module="model">
            <property name="movieFinder" ref="myMovieFinder"/>
        </object>
        <object name="myMovieFinder" type="examples.movieFinder.SimpleMovieFinder" module="dao"/>
        <object name="anotherMovieFinder" type="examples.movieFinder.ColonDelimitedMovieFinder">
            <property name="fileName" value="movies.txt"/>
        </object>
</objects>

Don’t hesitate to contact me for more information.

By ITERATIF - BUGALOTTO Olivier (2007). You can view this tutorial and its comments on my blog








Adobe Training center


Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42