Use the PicoContainer

PicoContainer is a highly embeddable, full-service, Inversion of Control (IoC) container for components honor the Dependency Injection pattern. The project started in 2003 and pioneered Constructor Injection auto-wiring. It is also Open Source and therefore free to use. The license is BSD and thus you can safely use this with commercial or other open source software.

You can get all the info about the original project  here.

The idea behind these classes is to follow the same philosophy also in Flash Platform based applications that now can use the ActionScript? 3.0 Pico Container implementation.

We was inspired from this project because we have grown to be thousands of classes, with dozens if of Singletons and we started to have some issues in the way we can work on tests with Singletons.

Once during a conference we get a sentence from a developer: "Why use Singleton, if you are the developer of a program you are aware about how many instances of a class you have created". It was funny because we believe that this can be true only if you are an independent developer and not a developer involved in a team and moreover the Singleton can be useful to call methods of the same instance from any component of an application.

At the end we totally disagreed with this sentence but we started to figure out a solution to have a way to define single instances of a class without using the Singleton design pattern.

The idea is to have a couple of classes with which you can have access to a container of components (i.e. connection drivers, logs manager, etc.) so we designed this classes:

 * PicoManager
 * PicoContainer


The first class is a class with the following static methods:

 * removeContainer(name:String):void
 * getAll():ICollectionView
 * addContainer(container:PicoContainer, name:String):Boolean
 * getPicoContainer(name:String):PicoContainer
 * refresh (exclude:Vector.<String>):void


In this way you can add a container (i.e. an instance of the PicoContainer class) to the manager and recover it by name anywhere in your application.
The PicoContainer class is the core of the implementation because it's the one that can store the components and that can be used to call methods on all the instance of a specific class or on a specific component and even more important you can register a Monitor so that you have a bridge for the events that are dispatched from the component stored in the container (i.e. PicoContainer).
The syntax to create a new container is

containerA = new PicoContainer();


After you have your container you can add an instance of a component with the following line of code (where goofy is a variable that store your component)

containerB.addComponent(goofy);


If you try now to add another instance of the same class you get an exception so in order to handle it properly (and just in case you really need another instance of the same class in the container) you can use a try...catch block an add it with a specific name

containerB.addComponent(goofy);
				
try{
					
	containerB.addComponent(goofy2);
					
}catch(e:Error){
					
		
	containerB.addComponent(goofy2, "additionalCompo")
					
}


Your container now contains two components, you can get them with the getAll() method of the PicoContainer class

containerB.getAll();


Now you can choose what you want to do with your component, you can simply keep them stored and recover them anywhere in your application with the following syntax

containerB.getComponent(clazz:Class, name:String = "")


Or you can call a method over all the same type of components in your container. Imagine to have stored in the PicoContainer some Canvas and that you want to call over all of them the drawFocus method, you have to say to the PicoContainer to be able to receive a visitor that can invoke a method

var visitors:Vector.<Class> = new Vector.<Class>();
visitors[0] = MehtodVisitor;
				
containerB.acceptVisitors = visitors;


Then you have to initialize the visit of the method to the container

var m:MehtodVisitor = new MehtodVisitor()
				
m.visit(containerB);


in order to be able to configure a method and invoke it through the visitor

var methodParams:Vector.<IPicoParameter> = new Vector.<IPicoParameter>();
methodParams[0] = new ComponentParameter("", true);
				
var realMethod:IPicoMethod = new PicoMethodBuilder().kind(new MethodConfiguration()).name("drawFocus").withArguments(methodParams).create();
				
m.invoke(realMethod)
// m.invoke(realMethod, Canvas);


The commented line invoke the method only over Canvas instance, the not commented invoke the method on all the components stored in the PicoContainer.
The last thing that you can do with the components stored in the PicoContainer is to create a Monitor (a monitor which delegates to another monitor. It provides a default ComponentMonitor?, but does not allow to use null for the delegate) in order to define a temporary listener for a specific set of events to which your component can react.
Imagine to work with canvases and to have the need to detect somewhere the mouse over event, you can do this simply with a monitor

mouseMonitor = containerB.monitorComponent(Canvas);
				
var eventsToListen:Vector.<Class> = new Vector.<Class>()
eventsToListen[0] = MouseEvent;
				
mouseMonitor.registerFor(eventsToListen);
				
(mouseMonitor as IEventDispatcher).addEventListener("rollOver", function(e:MouseEvent):void{
					
	trace(e);
					
});


In order to stop a monitor you can use the syntax

mouseMonitor.stop();


In order to delete all the reference to it you can use the syntax

mouseMonitor.dispose();


Added the 10.12.2009 the support for the properties set through a visitor'''

      

        var visitors:Vector.<Class> = new Vector.<Class>();
	visitors[0] = PropertyVisitor;
				
	containerB.acceptVisitors = visitors;
				
	var p:PropertyVisitor = new PropertyVisitor();
				
	p.visit(containerB);
				
	var parameter:ComponentParameter = new ComponentParameter("x", 10);
				
	p.define(parameter, Canvas);


The complete result of the demo can be get from the attache file instead here a complete listing of the code is reported

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import mx.events.ResizeEvent;
			import com.gnstudio.nabiro.utilities.pico.monitors.IMonitor;
			import com.gnstudio.nabiro.utilities.pico.helpers.ComponentParameter;
			import com.gnstudio.nabiro.mvp.core.Parameter;
			import com.gnstudio.nabiro.utilities.pico.helpers.IPicoParameter;
			import com.gnstudio.nabiro.utilities.pico.helpers.MethodConfiguration;
			import com.gnstudio.nabiro.utilities.pico.helpers.PicoMethodBuilder;
			import com.gnstudio.nabiro.utilities.pico.helpers.IPicoMethod;
			import com.gnstudio.nabiro.utilities.pico.visitors.IVisitors;
			import com.gnstudio.nabiro.utilities.pico.visitors.MehtodVisitor;
			import mx.containers.Canvas;
			import __AS3__.vec.Vector;
			import com.gnstudio.nabiro.utilities.HashMap;
			import com.gnstudio.nabiro.utilities.pico.PicoContainer;
			import com.gnstudio.nabiro.utilities.pico.PicoManager;
			
			private var containerA:PicoContainer;
			private var containerB:PicoContainer;
			private var containerC:PicoContainer;
			
			private var goofy:Canvas;
			private var goofy2:Canvas;
			
			private var resizeMonitor:IMonitor;
			private var mouseMonitor:IMonitor;
			
			private function createContainers():void{
				
				containerA = new PicoContainer();
				containerB = new PicoContainer();
				containerC = new PicoContainer();
				
				logText = "Containers created\n";
				
			}
			
			private function addContainers():void{
				
				
				PicoManager.addContainer(containerA, "aContainer");
				PicoManager.addContainer(containerB, "bContainer");
				PicoManager.addContainer(containerC, "cContainer");
				
				logText = "Containers: " + PicoManager.getAll() + "\n";
				
			}
			
			private function removeAll():void{
				
				PicoManager.removeContainer(PicoManager.REMOVE_ALL);
				
				logText = "Containers: " + PicoManager.getAll() + "\n";
				
			}
			
			private function removeSome():void{
				
				var exclude:Vector.<String> = new Vector.<String>()
				exclude[0] =  "bContainer";
				
				PicoManager.refresh(exclude);
				
				logText = "Containers: " + PicoManager.getAll() + "\n";
				
			}
			
			private function createRandomCanvases():void{
				
				goofy = new Canvas()
				
				goofy.width = 300
				goofy.height = 300
				goofy.x = 400
				goofy.setStyle("backgroundColor", 0xff0000);
				addChild(goofy)
				
				goofy2 = new Canvas()
				goofy2.percentHeight = 30;
				goofy2.percentWidth = 30;
				goofy2.setStyle("backgroundColor", 0x00ff00);
				goofy2.x = 600
				addChild(goofy2);
				
				logText = "Canvases created\n";
				
			}
			
			private function addCanvasesToContainer():void{
				
				containerB.addComponent(goofy);
				
				try{
					
					containerB.addComponent(goofy2);
					
				}catch(e:Error){
					
					logText = e.message + "\n";
					containerB.addComponent(goofy2, "additionalCompo")
					
				}
				
				logText = "Components: " + containerB.getAll() + "\n";
				
			}
			
			private function addResizeMonitor():void{
				
				resizeMonitor = containerB.monitorComponent(Canvas, "additionalCompo");
				
				var eventsToListen:Vector.<Class> = new Vector.<Class>()
				eventsToListen[0] = ResizeEvent;
				
				resizeMonitor.registerFor(eventsToListen);
				
				(resizeMonitor as IEventDispatcher).addEventListener("resize", function(e:ResizeEvent):void{
					
					logText = "Monitoring: " + e + "\n";
					
				});
				
			}
			
			private function addMouseMonitor():void{
				
				mouseMonitor = containerB.monitorComponent(Canvas);
				
				var eventsToListen:Vector.<Class> = new Vector.<Class>()
				eventsToListen[0] = MouseEvent;
				
				mouseMonitor.registerFor(eventsToListen);
				
				(mouseMonitor as IEventDispatcher).addEventListener("rollOver", function(e:MouseEvent):void{
					
					logText = "Monitoring: " + e + "\n";
					
				});
				
				(mouseMonitor as IEventDispatcher).addEventListener("mouseUp", function(e:MouseEvent):void{
					
					logText = "Monitoring: " + e + "\n";
					
				});
				
			}
			
			private function stopMouseMonitor():void{
				
				mouseMonitor.dispose();
				
				logText = "Monitoring mouse stopped\n";
				
			}
			
			private function stopMonitorResize():void{
				
				resizeMonitor.dispose();
				
				logText = "Monitoring resize stopped\n";
				
			}
			
			private function callMultipleMethods():void{
				
				var visitors:Vector.<Class> = new Vector.<Class>();
				visitors[0] = MehtodVisitor;
				
				containerB.acceptVisitors = visitors;
				
				var m:MehtodVisitor = new MehtodVisitor()
				
				m.visit(containerB);
				
				var methodParams:Vector.<IPicoParameter> = new Vector.<IPicoParameter>();
				methodParams[0] = new ComponentParameter("", true);
				
				var realMethod:IPicoMethod = new PicoMethodBuilder().kind(new MethodConfiguration()).name("drawFocus").withArguments(methodParams).create();
				
				m.invoke(realMethod)
				m.invoke(realMethod, Canvas);
				
				logText = "Multiple " + realMethod.name + " called on the " + containerB.getAll() + " components\n";
				
				
			}
			
			private function set logText(msg:String):void{
				
				logs.text += msg
				logs.verticalScrollPosition = logs.maxVerticalScrollPosition + 4;
				
			}

			
		]]>
	</mx:Script>
	<mx:Button x="10" y="10" label="create containers" click="createContainers()" width="180"/>
	<mx:Button x="10" y="40" label="add containers" click="addContainers()" width="180"/>
	<mx:Button x="10" y="70" label="remove all containers" click="removeAll()" width="180"/>
	<mx:Button x="10" y="100" label="remove some containers" click="removeSome()" width="180"/>
	<mx:Button x="10" y="130" label="create random canvases" click="createRandomCanvases()" width="180"/>
	<mx:Button x="10" y="160" label="add canvases to container" click="addCanvasesToContainer()" width="180"/>
	<mx:Button x="10" y="190" label="monitor resize" click="addResizeMonitor()" width="180"/>
	<mx:Button x="10" y="250" label="monitor mouse" click="addMouseMonitor()" width="180"/>
	<mx:Button x="10" y="280" label="stop monitor mouse" click="stopMouseMonitor()" width="180"/>
	<mx:Button x="10" y="220" label="stop monitor resize" click="stopMonitorResize()" width="180"/>
	<mx:Button x="10" y="310" label="call multiple methods" click="callMultipleMethods()" width="180"/>
	<mx:TextArea id="logs" height="154" left="10" right="10" top="360" />
	<mx:Label x="10" y="340" text="Logs" fontWeight="bold"/>
</mx:Application>

Attachments

1.1.2-pro © 2008-2009 agile42 all rights reserved (this page was served in: 0.31000 sec.)