Module extending_factory

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.

Usage

For an end consumer of Aedi library, it is rarely needed to extend it's logic with something new. But for a developer that wants to integrate Aedi library with his/her framework, sometimes it is required to extend library to integrate it with rest of framework. Aedi was designed with purpose of extending it in two directions:

  1. Containers -> responsible for created components.
  2. Component factories -> responsible for creating components

A component factory, as name states is responsible for creation and assembly of components registered in container, as well as destroying them. Containers are using them to instantiate components, that are requested. In all tutorials up to this registering a component, was equivalent to creating a factory for it, and registering it in container. Afterwards factory was returned to the client code, were it was further configured, with construction and setter injections by code.

When the default features of framework are not enough, it is possible to extend it, in several directions mentioned above. For defining a new way of creating components, a factory interface in listing below is shown, which is minimal requirement for having the custom factories useable by containers. A factory encapsulates the logic required as to create and destroy a component.

interface Factory(T) : LocatorAware!(), AllocatorAware!() {

	public {

		T factory();
        void destruct(ref T component);

		@property {
    		TypeInfo type();
		}
	}
}

For example purposes, during development of car simulation app, logging of car assembly should be done. Example below shows a simple logging component factory that creates components using zero argument constructor, and logs the construction using default logging facilities present in D programming language.

class LoggingFactory(T) : Factory!T {
    private {
        Locator!() locator_;
        IAllocator allocator_;
    }

    public {
        @property {
            typeof(this) allocator(IAllocator allocator) @safe nothrow pure {
                this.allocator_ = allocator;

                return this;
            }

            IAllocator allocator() @safe nothrow pure {
                return this.allocator_;
            }
        }

        T factory() {
            import std.experimental.logger;

            info("Creating component");
            static if (is(T : Object)) {
                auto t = this.allocator.make!T;
            } else {
                auto t = T();
            }
            info("Ended component creation");

            return t;
        }

        void destruct(ref T component) {
            import std.experimental.logger;

            info("Destroying component");
            static if (is(T : Object)) {
                this.allocator.dispose(component);
                info("Done destroying component");
            } else {
                info("Value component nothing to destroy");
            }
        }

        TypeInfo type() {
            return typeid(T);
        }

        LoggingFactory!T locator(Locator!() locator) @safe nothrow {
        	this.locator_ = locator;

        	return this;
        }
    }
}

A design decision in framework was made, that all containers must store only objects rooted in Object class. Since the language in which framework is developed supports not only object, but structs, unions and a multitude of value based types, those types should be wrapped into an object to be stored into containers. By convention, it is assumed that value based data is wrapped in an implementation of Wrapper interface which roots into Object class. Knowing this, factories that attempt to supply value based dependencies to components, fetch the wrapped components from container, extracts the component from wrapper and pass it to the component.

From the constraint that containers apply on accepted types (rooted in Object), the same requirements are propagated to component factories which are stored in containers. To leverage this problem, framework provides a decorating factory, that wraps up another factory, and exposes an compatible interface for containers. It will automatically wrap any component that is not rooted in Object class into a Wrapper implementation and give it further to container.

For aesthetic purposes it is recommended any creation of a factory to be wrapped in a function, which itself can serve as a facade to implementation, and be integrated seamlessly in code api. Example below shows an example of such a wrapper, that handles creation of a component factory and wrapping it in a compatible interface for containers.

auto registerLogged(Type)(Storage!(ObjectFactory, string) container, string identity) {
    auto factory = new LoggingFactory!Type;
    container.set(new WrappingFactory!(LoggingFactory!Type)(factory), identity);

    return factory;
}

To test custom component factory example below shows how it can be used seamlessly and unknowingly from the point of view of a user of library.

void main() {

    SingletonContainer container = singleton();

    container.registerLogged!Tire("logging.tire");
    container.registerLogged!int("logging.int");

    container.instantiate();

    container.locate!Tire("logging.tire").writeln;
    container.locate!int("logging.int").writeln;
}

Running the container we will get following result:

2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component
2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component
2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component
2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component
Tire(0 inch, nan atm, )
0

As we can see our logging factory is doing designed job. Try to implement your own factory, to understand how Aedi works behind the scenes.

Functions

NameDescription
main()
registerLogged(container, identity)

Classes

NameDescription
LoggingFactory
Tire