Is lazy dependency injection a good idea?
Tags: programming, c, net
Published: 2021-04-24 15:04:54

---
title: Is lazy dependency injection a good idea?
published: true
date: 2021-04-24 15:03:18 UTC
tags: programming,c,net
canonical_url: https://siderite.dev/blog/lazy-dependency-injection-good-idea/
---

Original post at: https://siderite.dev/blog/lazy-dependency-injection-good-idea/

## Intro

  When talking [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection "A form of Inversion of Control, it is a technique in which an object receives other objects that it depends on. These other objects are called dependencies"), if a class implementing _Interface1_ needs an implementation of _Interface2_ in its constructor and the implementation for _Interface2_ needs an implementation of _Interface1_ you get a circular dependency error. This could be fixed, though, by providing lazy proxy implementations, which would also fix issues with resources getting allocated too early and other similar issues.  Now, theoretically this is horrible. Yet in practice one meets this situation a lot. This post will attempt to clarify why this happens and how practice may be different from theory.

## Problem definition

  Let's start with defining what an interface is. Wikipedia says it's a [shared boundary between components](https://en.wikipedia.org/wiki/Interface_(computing) "a shared boundary across which two or more separate components of a computer system exchange information"). In the context of dependency injection you often hear about the [Single Responsibility Principle](https://en.wikipedia.org/wiki/Single-responsibility_principle "states that every module, class or function in a computer program should have responsibility over a single part of that program's functionality, and it should encapsulate that part"), which stipulates that a class (and by extension an interface) should only do one thing. Yet even in this case, the implementation for any of the Facade, Bridge, Decorator, Proxy and Adapter software patterns would do only one thing: to proxy, merge or split the functionality of other components, regardless of how many and how complex they are. Going to the other extreme, one could create an interface for every conceivable method, thus eliminating the need for circular dependencies and also loading code that is not yet needed. And then there are the humans writing the code. When you need a service to provide the physical location of the application you would call it _ILocationService_ and when you want to compute the distance between two places you would use the same, because it's about locations, right? Having an _ILocationProviderService_ and an _ILocationDistanceCalculator_ feels like overkill. Imagine trying to determine if a functionality regarding locations is already implemented and going through all the _ILocation..._ interfaces to find out, then having to create a new interface when you write the code for it and spending sleepless nights wondering if you named things right (and if you need to [invalidate their cache](https://quotesondesign.com/phil-karlton/ "There are only two hard things in Computer Science: cache invalidation and naming things. — PHIL KARLTON")).

  In other words, depending on context, an interface can be anything, as arbitrarily complex as the components it separates. They could contain methods that are required by other components together with methods that require other components. If you have more such interfaces, you might end up with a circular dependency in the instantiation phase even if the execution flow would not have this problem. Let's take a silly example.

  We have a _LocationService_ and a _TimeService_. One handles points in space the other moments in time. And let's say we have the entire history of someone's movements. You could get a location based on the time provided (_GetLocation_) or get the time based on a provided location (_GetTime_). Now, the input from the user is text, so we need the _LocationService_ and the _TimeService_ to translate that text into actual points in space and moments in time, so _GetLocation_ would use an _ITimeService_, while _GetTime_ would use an _ILocationService_. You start the program and you get the circular dependency error. I told you it would be silly. Anyway, you can split any of the services into _ITimeParser_ and _ITimeManager_ or whatever, you can create a new interface called _ITextParser_, there are a myriad refactoring solutions. But what if you don't have the luxury to refactor and why do you even need to do anything? Surely if you call _GetLocation_ you only need to parse the time, you never call _GetTime_, and the other way around.

## Solution?

  A possible solution is to only actually get the dependency implementation when you use it. Instead of providing the actual implementation for the interface you need, you provide a lazy proxy. Here is an example of a generic (and lazy one liner) LazyProxy implementation:

```
public class LazyProxy<TInterface>:Lazy<TInterface>
{
    public LazyProxy(IServiceProvider serviceProvider) : base(() => serviceProvider.GetService<TInterface>()) { }
}
```

  Problem solved, right? _LocationService_ would ask for a `LazyProxy<ITimeService>` implementation, _GetLocation_ would do `_lazyTimeService.Value.ParseTime(input)` which would instantiate a _TimeService_ for the first time, which would ask for a `LazyProxy<ILocationService>` and in _GetTime_ it would use `_lazyLocationService.Value.ParseLocation(input)` which would get the existing instance of _LocationService_ (if it's registered as [Singleton](https://en.wikipedia.org/wiki/Singleton_pattern "restricts the instantiation of a class to one "single" instance. This is useful when exactly one object is needed to coordinate actions across the system.")). Imagine either of these services would have needed a lot of other dependencies.

  Now, that's what called a "[leaky abstraction](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/ "The Law of Leaky Abstractions")". You are hiding the complexity of instantiating and caching a service (and all of its dependencies) until you actually use it. Then you might get an error, when the actual shit hits the actual fan. I do believe that the term "leaky" might have originated from the aforementioned idiom. Yuck, right? It's when the abstraction leaked the complexity that lies beneath.

  There are a number of reasons why you shouldn't do it. Let's get through them.

## Criticism

  The most obvious one is that you could do better. The design in the simple and at the same time contrived example above is flawed because each of the services are doing two very separate things: providing a value based on a parameter and interpreting text input. If parsing is a necessary functionality of your application, then why not design an _ITextParser_ interface that both services would use? And if your case is that sometimes you instantiate a class to use one set of functions and sometimes to use another set of functions, maybe you should split that up into two. However, in real life situations you might not have full control over the code, you might not have the resources to refactor the code. Hell, you might be neck deep in [spaghetti code](https://en.wikipedia.org/wiki/Spaghetti_code "unstructured and difficult-to-maintain source code")! Have you ever worked in one of those house of cards companies where you are not allowed to touch any piece of code for fear it would all crash?

  The next issue is that you would push the detection for a possible bug to a particular point of the execution of your code. You would generate a [Heisenbug](https://en.wikipedia.org/wiki/Heisenbug "a software bug that seems to disappear or alter its behavior when one attempts to study it."), a bug that gets reproduced inconsistently. How appropriate this would have been if an _IMomentumService_ were used as well. Developers love Heisenbugs, as the time for their resolution can vary wildly and they would be forced to actually use what they code. [Oh, the humanity!](https://youtu.be/0Ad9tholMEM?t=38 "Yeah, well, at least those people died fast. A bug can take weeks to solve!") Yet, the only problem you would detect early is the cycle in the dependency graph, which is more of a design issue anyway. A bug in the implementation would still be detected when you try to use it. 

  One other issue that this pattern would solve should not be there in the first place: [heavy resource use in constructors](/Resource%20acquisition%20is%20initialization "Maybe it IS a design requirement"). Constructors [should only construct](https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/ "Injection Constructors should be simple"), obviously, leaving other mechanisms to handle the use of external resources. But here is the snag: if you buy into this requirement for constructors you already use leaky abstractions. And again, you might not be able to change the constructors.

  Consider, though, the way this pattern works. It is based on the fact that no matter when you request the instantiation of a class, you would have a ready implementation of _IServiceProvider_. The fact that the [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern "uses a central registry known as the "service locator", which on request returns the information necessary to perform a certain task") mechanism exists is already lazy instantiation on the _GetService_ method. In fact, this lazy injection pattern is itself a [constructor dependency injection](https://freecontent.manning.com/understanding-constructor-injection/ "Understanding Constructor Injection") abstraction of the service provider pattern. You could just as well do `var timeService = _serviceProvider.GetService<ITimeService>()` inside your _GetLocation_ method and it would do the exact same thing. So this is another reason why you should not do it: mixing the metaphors. But hey! If you have read this far, you know that I love mixing those suckers up!

## Conclusion

  In conclusion, I cannot recommend this solution if you have others like refactoring available. But in a pinch it might work. Let me know what you think!

  BTW, this issue has been also discussed on [Stack Overflow](https://stackoverflow.com/questions/12284487/lazy-dependency-injection/12285441 "Lazy Dependency Injection"), where there are some interesting answers.