Created by Macrovector — Freepik.com

This is week 2 of Advent of Kotlin challenge. In last week we had many applications, and we can continue to learn more amazing things. All concrete rules of this challenge you can find in the previous article.

Kotlin, relatively young language, already has a large variety of dependency injection libraries (to name few: Koin, KodeinDI, Injekt, Kapsule). I absolutely understand it. Implementing this pattern was very challenging in Java or JS, and it is a nice and pleasurable challenge in Kotlin. This is why this week we are all going to implement a simple DI library. This task will help us learn few less known Kotlin features, practice DSL creation, and let us understand how these libraries work.

Notice: We won’t use annotation processing, and to be strict, we will implement Resource Loader, not Dependency Injection, same as all noticed libraries.

If you are not familiar with DI concept, here is a short introduction, here is a presentation by Salomon Brys, and here is a Dagger explanation that thought me DI initially years ago.

Class creators registry

The key part is the injection. We should allow direct or lazy injection:

(This is just an example. You can name this functions differently, and you can call them on some object instead of defining them as top-level functions.)

How is it possible that those inject functions know about property type? Thanks to type inference:

When we additionally make this function inline, and it’s type parameter reified, we will be able to operate on it as if we have a real type:

This way you can localize type from a registry of all class creators. How might this registry look like? The simplest way is to make some map that will associate a class name to creator method:

Remember that property can have a nullable type. How should it act then? Decide yourself.

DSL

To make injection possible, we first need to specify how each type should be created. It is usually done using some DSL. For instance, it is an example from Koin:

Here is an example from Kodein:

Here is an example from Injekt:

It is your decision how do you want this DSL to look like and behave. Notice one more difference, some libraries have one global register while some allow to have many registers and use injection on one of them. Both ways are good, but it would be nice to set both default register and have a possibility to specify different modules.

Task

Make your simple dependency injection and show some usage examples. You can inspire by Koin, KodeinDI, Injekt, and Kapsule. Make a dependency injection you will enjoy using. Have fun :)

Once you’re done, publish your implementation (for instance on Github or Github Gist) and share it on Twitter with the tag #AdventOfKotlin18 or send it to contact@kt.academy. By sending it there, you allow us to publish this code and your full name. We will publish the best solution and some honorable mentions here in the next Sunday (9 December) together with author name or nick.

Comment on the end

Should you implement your own DI in your project? The answer is: It depends but in most cases not. Having it implemented on your project gives you more freedom. On the other hand, using a mature and tested solution is safer and more convenient for new developers. This is why I generally recommend this option. In the worst case, all those frameworks are very similar, and migration is smooth when you use project-wide replace with Regex. Tested ;)

Results

We had many excellent submissions each represented its style and taste. Here are a few mentions:

Balázs Németh sent a well-tested, clean and very powerful solution.

Christian Fuchs sent a very short and concise solution.

Timothy Earley sent an original and way more OOP oriented solution.

Jose Ignacio Acin Pozo sent a concise, clean and tested solution (SO, Github).

On end, I decided to choose the solution by Olivier Perez, who not only made awesome injection and DSL, but also documented everything on Github. Here is his submission on Twitter:

A few additional elements we really liked:

Different kinds of factories — to create a new instance every time, once per module or once for all modules.

He used InjectionDsl annotation which is his DSL marker. It prevents using methods from InjectModule block inside internal lambda expressions.

annotation which is his DSL marker. It prevents using methods from InjectModule block inside internal lambda expressions. Creating classes to represent a different kind of generators (instead of collecting different kinds of lambda expressions).

Special error messages when a type is not declared in the DSL.

Let’s analyze it. Injections are located in modules. Here are examples of both lazy and normal injections:

He implemented different kinds of injections:

Simple and functional usage, but what about the code? It is as clear! Here is a DSL definition:

For those who are not familiar with DSL, module block is a lambda expression with a receiver. It means that braces after module is a lambda expression, but just like in extension function the extended object is the receiver so we can access it using this . In this solution, lambda expression also has a different receiver. In this case, the receiver type is InjectModule . Thanks to that we can directly use all methods from InjectModule , like factory , scope , or singleton . Those methods are invoked on an instance of InjectModule , and they modify its internal state — in this case by adding new ways to create an object. Then we need to get an object. We use to that the following methods:

It is a great solution I recommend it to anyone who wishes to understand how DSL works. It wasn’t an easy challenge and everyone who solved it is a winner. Congratulations!