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?
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.
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.
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.
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.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With