Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What if Dependency Injection is not possible?

After much kicking and screaming, I'm starting to accept DI despite how much cleaner SL may seem as dependencies grow.

However, IMO there's still a significant show-stopper with regards to DI:

DI is not possible when you don't have control over an object's instantiation. In the ASP.NET world, examples include: HttpModule, HttpHandler, Page, etc.

In the above scenario we would resort to static service location to resolve dependencies, typically via HttpContext.Current, which invariably infers scope from the current thread. So if we're going to use static SL here, then why not use it else where too?

Is the answer as simple as: grit your teeth and use SL when necessary (like above), but try and favor DI? And if so: doesn't using static SL just once potentially break the consistency of an entire application? Essentially undoing the hard work of DI everywhere else?

like image 291
Lawrence Wagerfield Avatar asked Feb 20 '11 21:02

Lawrence Wagerfield


People also ask

Is dependency injection really necessary?

The dependency injection technique enables you to improve this even further. It provides a way to separate the creation of an object from its usage. By doing that, you can replace a dependency without changing any code and it also reduces the boilerplate code in your business logic.

What is the alternative to dependency injection?

An alternative to dependency injection is using a service locator. The service locator design pattern also improves decoupling of classes from concrete dependencies. You create a class known as the service locator that creates and stores dependencies and then provides those dependencies on demand.

Why dependency injection is not good?

Disadvantages of Dependency Injection:Dependency injection creates clients that demand configuration details to be supplied by construction code. This can be difficult when obvious defaults are available. Dependency injection can make code difficult to trace (read) because it separates behaviour from construction.

Which dependency injection is not possible in spring?

With setter injection, Spring allows us to specify optional dependencies by adding @Autowired(required = false) to a setter method. This is not possible with constructor injection since the required=false would be applied to all constructor arguments.


1 Answers

Sometimes you just can't avoid tight coupling, like in your examples. However, that doesn't mean you need to embrace it either. Instead, quarantine it by encapsulating the messiness and factoring it away from your day-to-day life.

For example, if we want the current Forms Authentication user, we don't have much choice but to access HttpContext.Current.Request.User.Identity.Name. We do, however, have the choice of where to make that call.

HttpContext.Current is a solution to a problem. When we call it directly from where we use the results, we are declaring the problem and solution in the same place: "I need the current user name which is declared unwaveringly as coming from the current HTTP context." This muddles the definition of both and doesn't allow for different solutions to the same problem.

What we are missing is a clear articulation of the problem we are solving. For this example, it would be something like:

Determine the user which made the current request

The fact that we are using HttpContext.Current, or even a user name, is not a part of the core problem definition; it is an implementation detail that only serves to complicate the code requiring the current user.

We can represent the intent to retrieve the current user, sans implementation details, through an interface:

public interface IUserContext
{
    User GetUser();
}

Any class where we called HttpContext.Current directly can now use this interface instead, injected by the container, to maintain DI goodness. It is also much more intention-revealing for a class to accept an IUserContext in its constructor than to have a dependency which can't be seen from its public API.

The implementation buries the static call in a single place where it can't harm our objects any more:

public class FormsUserContext : IUserContext
{
    private readonly IUserRepository _userRepository;

    public FormsUserContext(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUser()
    {
        return _userRepository.GetByUserName(HttpContext.Current.Request.User.Identity.Name);
    }
}
like image 164
Bryan Watts Avatar answered Sep 21 '22 13:09

Bryan Watts