Module multiple_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.

In most simple applications, a single container, like singleton or prototype one might be enough, for using. Having a set of singletons constructed on behalf of a developer is convenient, yet there are occurences a single container is not enough. For example when, a set of components rely on a particular dependency, and more specifically, the dependency they rely upon, should not be shared across reliant components. In another case, a set of components are stored in a database, in serialized form, and they are used as dependencies for other components from application.

In such cases use of a single container like singleton or prototype is not enough, since one set of components should be created by using normal frameworks means, and another set needs to be fetched from a database, or should not be shared to all dependent components. To solve this problem, AEDI framework, allows container to be joined and used together, for creation of components. A component in such a joint container, having a dependency on a prototype component, will have it’s requirement fullfilled without a problem.

Using as an example car simulation app, the company decided to add a set ”tires” to simulated cars. For a particular car, each tire installed in it has same properties as other installed in same car. Registering 4 times same tire is not cost effective. Instead of it, it is better to register component into a prototype container and use component to supply 4 instances of a tire to a particular car. Example below shows how two or more containers can be joined together, configured with components, and used to instantiate required components.

auto container = aggregate( // Create a joint container hosting other two containers
        singleton(), "singleton", // Create singleton container, and store it in joint container by "singleton" identity
        prototype(), "prototype" // Create prototype container, and store it in joint container by "prototype" identity
    );

with (container.configure("singleton")) { // Configure singleton container

    // ...

    register!Car
        .autowire
        .autowire!"color"
        .set!"frontLeft"(lref!Tire)
        .set!"frontRight"(lref!Tire)
        .set!"backLeft"(lref!Tire)
        .set!"backRight"(lref!Tire);
}

with (container.configure("prototype")) { // Configure prototype container

    register!Tire // Registering Tire into "prototype" container used by aggregate container
        .set!"size"(17)
        .set!"pressure"(3.0)
        .set!"vendor"("divine tire");
}

//...

To join one or more containers together, an aggregate container must be created that will host both of them under the hood. Once aggregate container has all of joint containers registered in it, the process of registering components takes place. To register components in joint container, pass the identity of subcontainer (container in joint container) to configure as an argument, and register components for selected subcontainer. The configure function applied to joint container in with () statement creates a configuration context, that stores the container where components are stored, and container from which dependencies for those components should be fetched. In case of joint container, and singleton subcontainer, singleton subcontainer acts as storage while joint container is used as source for dependencies of registered components.

In the result, the car simulator will be able to use a car, that has 4 different instances of a tire. Output below shows the constructed car by joint containers.

Uuh, what a nice car, Electric car with following specs:
Size:	Size(200, 150, 300)
Color:	Color(0, 0, 0)
Engine:	app.ElectricEngine
Tire front left:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31180
Tire front right:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C311C0
Tire back left: 	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31200
Tire back right:	Tire(17 inch, 3 atm, divine tire)	 located at memory 7FB560C31240

Notice that each tire is a different object, hence everything is working as we expected!

Try this example, modify it, play with it to understand how compositing can help in designing your application.

Functions

NameDescription
drive(car, name)
main()

Interfaces

NameDescription
Engine Interface for engines.

Classes

NameDescription
Car A class representing a car.
CarManufacturer A manufacturer of cars.
DieselEngine A concrete implementation of Engine that uses diesel for propelling.
ElectricEngine A concrete implementation of Engine that uses electricity for propelling.
GasolineEngine A concrete implementation of Engine that uses gasoline for propelling.
Tire Tire, what it can represent else?

Structs

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