Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Alternative Flex DI Framework

tree: 4a01b36588

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 doc
Octocat-spinner-32 source
Octocat-spinner-32 test
Octocat-spinner-32 .gitignore
Octocat-spinner-32 README.mdown
README.mdown

Zero

Zero is a new way to approach Dependency Injection in Actionscript. It is a toolkit that replaces the concept of the Interface with the DependencyInterface, which defines an interface using Decorators instead of an ActionScript Interface.

In this document, Interface refers to an actionscript interface, while "interface" refers to the concept of an interface. The words "extend", "override", and "implement" refer to the concepts, not the Actionscript keywords.

Glossary

Use Cases

Goals

Questions

Getting Started

Visit Downloads and get the latest swc.

Contribute

Zero is open source. Please hack on it

http://github.com/seanhess/zero

DependencyInterfaces

Instead of a normal AS Interface, you create a simple class that defines an interface. Here is how to define a function.

public function doSomething(param:String):Boolean { return i.doSomething(param) }

You can also define getters and setters

[Bindable]
public function set name(value:String):void     { i.name = value }
public function get name():String               { return i.name }

You can specify events by putting an [Event] tag on the class

[Event(name="anEvent", type="flash.events.Event")]

Here is a full example

package dependency 
{
    [Event(name="newBook", type="flash.events.Event")]
    public class DLibrary extends DependencyInterface
    {
        /**
         * Passing the context to the constructor is required
         */
        public function DLibrary(context:*)         { super(context) }

        public function get books():IList           { return i.books }

        public function addBook(title:String):void  { i.addBook(title) }
    }
}

You can use arguments to simplify passing the arguments along

public function manyArguments(one:SomeClass, two:String, three:int):void { i.manyArguments(arguments) }

Using Dependencies

You use the new operator anywhere as if you were creating the class. Zero takes care of the rest.

// "this" is the context
var library:DLibrary = new DLibrary(this); 

Don't worry, you are not creating a dependency on the implementation. Think of the DependencyInterface as a normal AS Interface. DependencyInterfaces are easy to refactor to Interfaces and vice versa.

You can create them in MXML as well.

<!-- Automatically sets the context -->
<dependency:DLibrary id="library"/>

Implementations

There are no constraints on implementations at all. You don't event have to implement all the functions.

package service
{
    public class Library
    {
        /**
         * books is immutable because the interface only defines
         * a getter
         */
        public var books:IList = new ArrayCollection();

        public function addBook(title:String):void 
        {
            books.addItem(title);
            trace("Added " + title + " to library");
            dispatchEvent(new Event());
        }
    }
}

Connecting Implementations

You can easily connect an implementation to a DependencyInterface using ActionScript.

import dependency.DLibrary;
import service.Library;

var connect:Connect = new Connect(this);
connect.add(new Implement(DLibrary, Library));

You can do it in MXML as well

<zero:Connect>
    <zero:Implement dependency="{DLibrary}" factory="{Library}"/>
</zero:Connect>

Multiple Implementations

A single class doesn't need to be responsible for the whole interface.

package service
{
    public class Books
    {
        public var books:IList;
    }
}

package service
{
    public class BookCreator
    {
        public function addBook(title:String):void
        {
            trace("Do something different");
        }
    }
}

You can then connect both of them to the interface. Later implementations have priority.

var connect:Connect = new Connect(this);
connect.add(new Implement(DLibrary, Books));
connect.add(new Implement(DLibrary, BookCreator)); 

or in MXML

<zero:Connect>
    <zero:Implement dependency="{DLibrary}" factory="{Books}"/>
    <zero:Implement dependency="{DLibrary}" factory="{BookCreator}"/>
</zero:Connect>

Factories

You can define factories for short-term objects, like value objects or models.

// interface
public class DBook extends DependencyInterface
{
    public function get title():String           { return i.title }
    public function set title(value:String):void { i.title = value }
}

// implementation
public class Book
{
    public var title:String
}

And you tell Zero to connect them like this

connect.add(new Factory(DBook, Book));

or in mxml

<zero:Connect>
    <zero:Factory dependency="{DLibrary}" factory="{BookCreator}"/>
</zero:Connect>

You can then use the new operator, like normal, but Zero creates a new instance every time.

var book:DBook = new DBook(this);

or in mxml

<dependency:Book title="here is a title"/>

Proxying

You can use a Proxy to extend existing functionality. If you only need to override functionality, use multiple implementations instead.

public class LibraryProxy extends ObjectProxy
{
    public function addBook(title:String):void
    {
        trace("Do something cool");
        object.addBook(title);
        trace("Do something even cooler!");
    }
}

They are connected similarly

var connect:Connect = new Connect(this);
connect.add(new Implement(DLibrary, Library));
connect.add(new Proxy(DLibrary, LibraryProxy));

Dependencies in Implementations

Any class can ask for a dependency. Here's our Library example revisited, using the DBook factory

public class Library
{
    /**
     * books is immutable because the interface only defines
     * a getter
     */
    public var books:IList = new ArrayCollection();

    public function addBook(title:String):void 
    {
        var book:DBook = new DBook(this);
            book.title = title;

        books.addItem(book);
        trace("Added " + title + " to library");
    }
}

Disconnecting an Interface

You can disconnect an interface by calling its disconnect method.

library.disconnect();

And reconnect it with connect

library.connect();

Disconnecting a View

You can disconnect an entire context

var connect:Connect = new Connect(this);

// disconnects everything inside this context
connect.disconnect(); 

// disconnects some other context
connect.disconnect(someOtherObject);

// disconnects every context that matches the pattern
connect.disconnect("view.*");

Implementation by View

You can specify a different implementation for everything within a particular view by putting new connect instructions in it.

<!-- MainApplication.mxml -->
<zero:Connect>
    <Implement dependency="{DLibrary}" factory="{Library}"/>
</zero:Connect>

<!-- SomeOtherView.mxml -->
<zero:Connect>
    <Implement dependency="{DLibrary}" factory="{ADifferentLibrary}"/>
</zero:Connect>

Implementation by Other Context

The same applies for any context, actually, just define the connect instance with the other class as the context.

var connect:Connect = new Connect(someOtherObject);
    connect.add(...);

Implementation by Class

You can pass a class into Connect instead of a reference

import view.SomeView;

var connect:Connect = new Connect(SomeView);
    connect.add(...);

Implementation by Package

You can pass in a string with wildcards too.

var connect:Connect = new Connect("view.admin.*");
    connect.add(...);

Remove Implementations

You can use Connect's remove function to remove an implementation instruction

var lib:Implement = new Implement(DLibrary, Library);

var connect:Connect = new Connect(this);
    connect.add(lib);
    connect.remove(lib); 

Updating Implementations

Remember, you can override an implementation simply by adding a new one (Later implementations have higher priority)

You can remove an instruction and add a new one, or you can simply update the properties of an existing one

var lib:Implement = new Implement(DLibrary, Library);

var connect:Connect = new Connect(this);
    connect.add(lib);

    lib.factory = AnotherLibrary; // updates all interfaces connected to it. 

Testing and Mocking

Dependencies will not connect unless you specify a context. You can deliberately skip that step, and pass in your own.

var library:DLibrary = new DLibrary(); // I left out the context!
library.implementation = new MockLibrary();

You can also connect an implementation by package or class as described above. You can wipe out all information in the register. This makes it easy to ensure there is no stale information in the register.

var connect:Connect = new Connect(this);
connect.wipe();
// add pristine implementations

Glossary

DependencyInterface

A DependencyInterface is a class that wraps, or proxies, another object - also called a Decorator. However, unlike most Decorators, a DependencyInterface does not add any functionality. It only defines the functions that are available to call, in effect, performing the same role as an Interface.

Context

Objects in Zero need to know their context. DependencyInterfaces need to know which object created them, or which object is asking for a particular dependency. Connect has the ability to define different rules

Something went wrong with that request. Please try again.