Letting classes depend on abstractions instead of implementations makes a system loosely-coupled. Loose coupling has a few very clear advantages. Dependency Injection Principles, Practices, and Patterns defines the following advantages (chapter 1.2.2, table 1.1):
- Late binding: Services can be swapped with other services without recompiling code.
- Extensibility: Code can be extended and reused in ways not explicitly planned for.
- Parallel development: Code can be developed in parallel.
- Maintainability: Classes with clearly defined responsibilities are easier to maintain.
- Testability: Classes can be unit tested.
This doesn't mean however that abstractions are always useful. For instance, in case:
- The dependency is living in the same module as the consumer.
- The consumer and dependency don't need to be tested independently.
- The dependency is a Stable Dependency rather than a Volatile Dependency (see chapter 1.3.1).
- The dependency will never have to be Intercepted. A common reason for interception is to be able to able cross-cutting concerns.
I see limited benefit for interfaces that have one single implementation.
When you have one implementation, this typically means that:
- You don't fake the dependency for testing
- You don't intercept its behavior to apply cross-cutting concerns
But you need to be sure you don't need testability, extensibility and late binding for that particular dependency.
Another way to look at this is from the Reused Abstraction Principle. Having a system with many one-to-one mappings between abstractions and implementations is a code smell. In the systems I write, I typically have a handful of abstractions that are implemented by 90% of the classes in my system (this model is explained in detail in chapter 10 of DI in .NET).
I am against the usage of marker interfaces.
Marked interfaces can be useful, but not from the perspective of Dependency Injection, since a marked interface does not have any members, and it is therefore useless to inject a marked interface into a consumer.
The only reason why they are doing that is to benefit from the DI framework in injecting instances in constructors and maybe parameters/properties.
I would say this statement is incorrect. DI libraries typically don't care about whether the dependency is a concrete type or an abstraction. They happily inject it for you anyway. You start injecting abstractions because of the benefits of loose coupling.