Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection & using interfaces?

People also ask

What is the dependency injection?

In object-oriented programming (OOP) software design, dependency injection (DI) is the process of supplying a resource that a given piece of code requires. The required resource, which is often a component of the application itself, is called a dependency.

What is dependency injection example?

What is dependency injection? Classes often require references to other classes. For example, a Car class might need a reference to an Engine class. These required classes are called dependencies, and in this example the Car class is dependent on having an instance of the Engine class to run.

What is the benefit of dependency injection?

Dependency injection moves the dependencies to the interface of components. This makes it easier to see what dependencies a component has, making the code more readable. You don't have to look through all the code to see what dependencies you need to satisfy for a given component.


Letting your application components (the classes that contain the application logic) implement an interface is important, since this promoted the concept of:

Program to an interface, not an implementation.

This is effectively the Dependency Inversion Principle. Doing so allows you to replace, intercept or decorate dependencies without the need to change consumers of such dependency.

In many cases developers will be violating the SOLID principles however when having an almost one-to-one mapping between classes and an interfaces. One of the principles that is almost certainly violated is the Open/closed principle, because when every class has its own interface, it is not possible to extend (decorate) a set of classes with cross-cutting concerns (without dynamic proxy generation trickery that is).

In the systems I write, I define two generic interfaces that cover the bulk of the code of the business layer. They are called ICommandHandler<TCommand> and an IQueryHandler<TQuery, TResult>:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

Besides the nice side effect of not having to define many interfaces, this allows great flexibility and ease of testing. You can read more about it here and here.

Depending on the system I write, I might also use interfaces such as:

  • IValidator<T> for validating messages
  • ISecurityValidator<T> for applying security restrictions on messages
  • IRepository<T>, the repository pattern
  • IAuthorizationFilter<T> for applying authorization/security filtering on IQueryable<T> queries.

Depending on the system I write, somewhere between 80% and 98% of all components implement one of these generic interfaces I define. This makes applying cross-cutting concerns to those so called joinpoints trivial.


If you don't design to interfaces, you are going to be hamstrung when it comes time to refactor your code and/or add enhancements. Using a DI framework is not really at issue when it comes to designing to an interface. What DI gives you is late-binding and much better ability to write unit tests.