Use of principles of IoC/DI in modern web applications or "you get what you need"
Inversion of Control (IoC - “reverse control)“ is a way of creating a program code when its required dependencies are set for it from the outside, by its environment which uses it instead of the program obtaining them itself or creating them itself. Programming environments (frameworks and application servers) implement support for IoC according to design pattern Dependency Injection (DI - „inserting dependencies“).
In the application environment that supports IoC dependency injection or inserting dependencies is usually a task of a specialized component, so called DI container, which is an implementation of a design pattern factory and which provides creation (provision) of instances of reusable objects to which it then transmits the required dependent objects in one of three ways.
Possible ways to inject dependencies
- Constructor injection – dependencies are set by parameters of the constructor
- Setter (property) injection – a dependency is injected by a setter or directly by setting a public property of the object (if the programming language allows that)
- Interface injection – the class implements an interface which defines injected dependencies in a form of setters
Given framework (container) then may support one or more of the above.
A great advantage of injection by constructor is a guarantee of availability of all necessary dependencies already at the time of object creation. Nevertheless constructor injection may run into problems of reducing life-cycle of objects in application environments with limited lifetime of objects, when it can cause problems when serializing (passivization). In such cases, setter/property injection may be more advantageous when member fields (property) containing the dependency are volatile (not serialized) and the framework will ensure their re-setting within the process of deserialization (activation) of an object. However this principle of inserting dependencies cannot guarantee setting of all the dependencies at the same time (although this is usually no problem for DI container, in a multi-thread environment, the thread setting the dependencies can be synchronized with the object on which the injection is carried out) and may require support for implementation of post-create (post-init) method, that set (reset) the state of an object only after all the necessary dependencies were provided for it. Implementation of such “late initialization” (lazy initialization) performing method can also be directly connected to application of design pattern “lazy loading”, when injected components are brought to a usable state no sooner than when some public method is called on it. This is particularly important in the case of business components using external (remote) resources (typically connection to a network/database service). Lazy loading may also directly implement a framework by inserting dependencies in the form of proxy objects that instantiate (activate, deserialize) a dependent object only after calling of come method to which all calls are then delegated.
As DI is mainly used on an application layer in a scheme of a three-layer architecture for interconnection of particular separate business components by the dependencies, DI often also functions as a service locator (manager) or closely cooperates with it. Availability of IoC is an advantage on the presentation layer as well, which needs to have access to business components, thus a direct support of DI for front controllers (presenters), if the given web framework is built on the principle of MVC or for component model of user interface (UI).
DI may be controlled declaratory (for instance by annotation by using annotations or in a separate configuration file in XML or YML format) or by convention (by naming methods of variables used for the purpose of injection).
Applying the principle of CoC (Convention over Configuration) in implementation of DI in the given environment (framework) simplifies and for mapping of the dependencies of the components it does not require anything more than their actual source code (declaration of methods, or rather properties of an object). The stated principle can be seen for instance in frameworks Grails or CMS LARS Vivo.
Advantages and risks of using IoC
The benefits include
- Better clarity of the code – it is clear at first glance what dependencies the class (object) require for its functionality.
- More simple structure of the application – application of the principles of IoC leads to the creation of reusable components.
- Re-usability – classes (components) using DI for setting dependencies are more simple to re-use in other environments or applications.
- Better test-ability– classes written for IoC can be better tested and test mock objects can be created for them.
Disadvantages (risks) are
- For a developer who is not familiar with the principles of IoC, it may initially be difficult to orient themselves in a code written in such way.
- Usage of DI container can affect the speed of application, especially in environments that do not support multi-thread processing, or rather application scope where dependencies between objects must be adequately maintained.
Application of principles IoC with support of DI container is now more or less standard in all modern application environments aimed at supporting creation of rapid web applications and information systems. Support of IoC in most widespread frameworks (Symfony2, Zend Framework 2, Nette) is nowadays available for developers using web frameworks on platform PHP and they can actively use it in connection with support for creation of reusable components of service (application) layer of architecture of web systems. Despite higher computing demands the application of IoC means a significant shift in the quality of the object code of web solutions.