Home > Software design >  Dependency Injection: could it make your code harder to change down the line?
Dependency Injection: could it make your code harder to change down the line?

Time:12-17

From what I understand, basic dependency injection means that instead of creating a dependency inside a class, you make it outside and pass it in as a parameter.

So let's say you had a class Logger which does some stuff and then writes to a log and it depends on a WriteToFile object. Instead of creating that WriteToFile object inside the Logger class, you create it outside and pass it in as a parameter everytime you create a new Logger instance.

The thing which confuses me is imagine there's 1,000 places in your code where you create a Logger object and imagine for some reason you no longer need to use the WriteToFile object in your Logger class...

Without DI, you would just have to remove the code in the Logger class which creates a WriteToFile object and uses it. This might take a couple of seconds.

But with DI, you have to find those 1,000 places where you created the Logger object and remove the code where you created the WriteToFile object and passed it in as a parameter.

Is that correct or am I missing something important?

CodePudding user response:

Applying DI doesn't necessarily mean that you turn classes completely inside out, in such way that all a class's dependencies are supplied from the outside. That would easily result in an unmaintainable mess.

This is why, from the perspective of DI, we separate dependencies in 2 distinct groups:

  • Stable Dependencies: are classes (and functionality) who's behavior is deterministic, and that you never expect to have to replace, wrap, decorate or intercept.
  • Volatile Dependencies: All dependencies that are not stable, are by definition volatile. These are classes (and functionality) who's behavior is either nondeterministic (e.g. Random.Next, DateTime.Now, or when calling a database), or are parts of your code base you wish to be able to replace, wrap, decorate, or intercept.

There is actually more to Stable and Volatile Dependencies. A more-detailed description of these concepts can be found here.

From the perspective of DI, we are only interested in Volatile Dependencies. These are the dependencies that we wish to hide behind an abstraction, and inject through the constructor. Abstracting and injecting Volatile Dependencies gives many interesting advantages, such as:

  • Flexibility/maintainability. e.g. by depending on an ILogger you write your logs to the database instead of a file by changing a single line of code (or flipping a configuration switch).
  • Testability: By allowing Volatile Dependencies to be replaced, it becomes much easier to test a single class in isolation.

Stable Dependencies, on the other hand, don't need to be swapped by definition, and tight coupling with them does not hinder testability, because their behavior is deterministic.

If we look at your specific case, your logger is a good example of a Volatile Dependency:

  • Loggers are typically classes that you wish to replace in a plugin like model; tomorrow you might want to log to a database instead.
  • Your logger writes to disk; this is nondeterministic behavior.
  • You might want to replace the logger with a fake implementation when testing.

This means that most classes in your system should not take a dependency on your logger implementation, but rather take a dependency on a logging abstraction.

Your logger class contains, as you explained, a WriteToFile object. To understand whether or not it should be supplied to the logger from outside using DI, means you need to find out whether or not WriteToFile is -from perspective of the logger- a Volatile Dependency. Likely this WriteToFile object is an intrinsic part of your logger class. Together they form a single component. Classes within a single component are often expected to be tightly coupled. In that case, from perspective of the logger, WriteToFile is a Stable Dependency.

After you determined WriteToFile to be a Stable Dependency from perspective of your logger, there is no need to hide it behind an abstraction and inject it into the Logger. That would only introduce overhead without adding any benefits.

Applying DI to Stable Dependencies "make[s] your code harder to change down the line," while applying DI on Volatile Dependency makes your code easier to maintain.

  • Related