Module extending_containers

Aedi, a dependency injection library.

Aedi is a dependency injection library. It does provide a set of containers that do IoC, and an interface to configure application components (structs, objects, etc.)

Aim

The aim of library is to provide a dependency injection solution that is feature rich, easy to use, easy to learn, and easy to extend up to your needs.

Extending factories tutorial explained that library can be extended in two ways, factory direction and container direction. In AEDI framework containers have at least two responsibilities. The first one is to serve components, and the second one is to manage life time of served components.

Due to the first responsibility, AEDI has two basic containers, singleton and prototype. The behavior of those two containers can be refound in other implementations of DIF like Spring frame- work. Singleton container will serve same component no matter how many times a caller asks it for a component associated with desired identity. Prototype container will serve new instance of component each time it is asked by caller. Switchable decorating container presented decorated containers tutorial limits in some sense the lifetime of it’s components only to the period when it is enabled.

A custom container should be implemented, only when existing ones are not providing features required by the third party code (code that uses the framework). As stated, the purpose of a container is to serve and manage components, and therefore a new implementation of a container should be concerned with those tasks.

Compared to implementing a factory component, implementing a new container, requires it to implement several interfaces. Each interface provides some functionality that can be used by framework to enable some additional features related to container functionality. Implementation of those interfaces is not imposed. The only requirement is to implement the basic Container interface that exposes component serving abilities, along with option to instantiate them before their actual usage. The following interfaces are allowed to be implemented by custom container:

  • Container – exposes serving component features.
  • Storage – exposes component factory storing capabilities.
  • FactoryLocator – exposes component factory locating capabilities
  • AliasAware – exposes aliasing of component’s identity to another identity
  • ConfigurableContainer – exposes component factory storing capabilities along FactoryLocator and AliasAware

Container

Container interface is the minimal requirement for a custom container to implement, since it offers component serving capabilities. Listing below presents the interface that is to be implemented by custom containers. It is meaningful to implement only this interface when, container will not integrate with any advanced features that framework provides.

interface Container : Locator!(Object, string) {

    public {

        Container instantiate();

        Container terminate();
    }
}

Storage

By implementing Storage interface, a container declares it’s ability to store component factories in it. Listing below displays abilities required to be implemented by container. Once a container implements the interface, the component registration system can use the container to store factories in it. In other words it is possible to use register method on custom container. The interface does not enforce, how stored components are used internally by container. They can never be used, or used for other purposes, rather than serving created components to caller. Though, by convention it is assumed that stored factory components are used for right purpose of creating components served by container, and deviating from this convention, will lead in ambiguity in code api that must be documented exhaustive.

interface Storage(Type, KeyType) {

    public {

        Storage set(Type element, KeyType identity);

        Storage remove(KeyType identity);
    }
}

FactoryLocator

FactoryLocator interface, implemented by a container, exposes ability for exterior code to access stored factory components in a container. Listing below shows the abilities that a container must implement.

Such functionality is useful for postprocessing of component factories after they are registered into container, for various purposes. One of them, is dynamically register tagged components as a dependency to an event emmiter component. The FactoryLocator functionality allows for third party library developers, to implement more complicated and useful features based on postprocessing of factory components.

interface FactoryLocator(T : Factory!Z, Z) {
    import std.range.interfaces;
    import std.typecons;

    public {

        T getFactory(string identity);

        InputRange!(Tuple!(T, string)) getFactories();
    }
}

AliasAware

AliasAware implemented by a containers, exposes an interface to exterior by which aliasing of identities becomes possible. Listing below shows the methods that a container should implement. The aliasing of component identities can become handy, when a library provides a container with components, and another third party code is reliant on a component from this container, yet the dependency is referenced with another identity. Aliasing will allow the referenced identity to be aliased to right identity of component. Such cases can occur when a component from a library is deprecated and removed, and it’s functionality is provided by another one identified differently. In the end the aliasing mechanism allows a component from container to be associated with multiple identities.

interface AliasAware(Type) {

    public {

        AliasAware link(Type identity, Type alias_);

        AliasAware unlink(Type alias_);

        const(Type) resolve(in Type alias_) const;
    }
}

ConfigurableContainer

ConfigurableContainer interface is amalgation of previosly defined interfaces. Instead of declaring entire list of interfaces a container should implement, it is easier to replace it with ConfigurableCo interface. Listing below shows the abilities that a container must implement

interface ConfigurableContainer : Container, Storage!(ObjectFactory, string), AliasAware!(string), FactoryLocator!ObjectFactory {

}

Try to run example. Modify it, run it again to understand how to implement your own container.

Functions

NameDescription
drive(car, name)
main()

Interfaces

NameDescription
Engine Interface for engines.

Classes

NameDescription
Car A class representing a car.
DieselEngine A concrete implementation of Engine that uses diesel for propelling.
GasolineEngine A concrete implementation of Engine that uses gasoline for propelling.
MySingletonContainer

Structs

NameDescription
Color A struct that should be managed by container.
Size Size of a car.