Dependency Injection approach allows you to make your business application architecture flexible and extensible. This approach is widely known, and each language has many frameworks for implementing such an architectural solution. For example, Dagger and related components in Spring framework for Java, Microsoft Unity Framework for C#, Python Inject for Python. C++ language is not an exception and has a number of libraries for implementing dependencies.

Semantics of Inversion of Control, Dependency Injection, Dependency Container

The main idea of Inversion of Control (Inversion of Control, hereinafter – IoC) is that the programmer invokes the logic of linking and calling various components of the system not directly, but through the use of IoC-container. Such logic includes, for example, object creation, logging, caching, exception handling, and domain operation calls. Unlike the classical approach, in which the programmer has full control over all method and function calls, IoC allows delegating the execution of part of the business logic to a third-party framework.

The approach has the following goals:

  • increasing the flexibility of the system;
  • increasing atomicity of modules and entities;
  • simplifying the process of replacing individual modules.

Dependency injection (DI) is one of the ways to implement the IoC approach. The main principle of DI is to build a client – service relationship. Client is a certain component of the system (method, module, entity) that needs a third-party component – service – to realize its logic. In this case, the client does not search for and does not create the necessary component, but receives it from the outside, from the Dependency Container (hereinafter – DC).

Demonstration project

For practical demonstration of the capabilities of the frameworks under consideration, we have developed a simple program modeling a banking system. To build it, you will need boost library and C++ compiler with C++14 support. There are two branches in the repository: master contains DI implementation using kangaru, di_dependency uses [Boost].DI.

Clients do not interact with the model directly, but “communicate” with it via AccountService and DepositService. Interaction with the data layer takes place through the corresponding repository interfaces – AccountRepository and DepositRepository.

And this is where DI comes into the process. Working with abstractions, we can replace the implementation of this or that component at any time. For example, during testing we can use a repository that manages a predefined data set, in production we can use a repository to manage a real database; we can use a local implementation of services during testing, gRPC – in production.

[Boost].DI

A colleague of mine used to say, “If something is not in the STL, look in boost”. Of course, boost provides us with a framework to implement dependencies efficiently, but it doesn’t come with an official build yet. Like kangaru, [Boost].DI is a header-only library, and according to its authors, it is quite fast compared to similar libraries. The features of [Boost].DI also include:

the ability to explicitly control the lifetime of objects created by a dependency container (see Scopes);
control of container behavior, e.g. choosing where objects are allocated (stack or heap, see Providers);
using constraints when populating the container (see Concepts).

As with kangaru, the central object in [Boost].DI is the dependency container. In [Boost].DI, it is called injector. Unlike kangaru, its usage is more concise: no additional declarations and annotations are needed to describe dependencies.