Dependency Injection

Dependency Injection (DI) is an implementation of the Dependency Inversion Principle. {Not going to get into the turtles again} The principle itself states that high level modules should not be dependent on lower level modules but instead both should be dependent on abstractions. Also abstractions should not depend on details, the details should depend on the abstractions. The high level modules should be independent of the implementation details of the low level. The low level modules should be designed with the interaction in mind as it may need to change interfaces. However, the inversion of dependency does not mean that lower level layers depend on higher level layers. Both should depend on the abstract interface between them. This reduces coupling of components without adding more code or coding patterns.

Dependency Injection has three main responsibilities. Most important is the creation of objects. Next is knowing which classes require the objects it creates. Finally it provide the classes all the necessary objects to function.The goal of dependency injection is to separate the usage an object from it’s creation. This removes a class’s direct dependency on another class. Dependent classes can be changed out without the depending class knowing or caring.

This is a deep dive into dependency injection specifically. This doesn’t go into platform or language specifics. It’s up to you to now go and find how the language and platforms you work with apply these concepts. Just about any object oriented language will have a way to apply dependency injection, most of the more common ones will have pre-build frameworks you can use. The information provided in this episode can help you in determining if you need one and which one to use in your code.

Episode Breakdown

14:05 Roles In Dependency Injection

The service is the object that is depended upon in order for the module to function. Any object or class that can be used by another is considered a service object. This is the object we want to decouple from the calling object or class.

The client is the module of code being called that is dependent on the service. Any object or class that uses another is considered a client object. This is the object we want to decouple from it’s dependencies. It should not know anything about the service objects it uses.

Interfaces define how the client is able to use and interact with the service. This should be all the client sees or knows about the service object. Interfaces define the methods available to the client. This includes what is passed into each method. It also contains what is returned by the method. No implementation details or how the service method works are involved in interfaces. Interfaces may also be abstract or concrete classes. The latter violates dependency inversion principle, though. It removes the dynamic decoupling that allows for unit testing. However, the client would not ever construct them so they would not be treated as concrete.

The calling code responsible for constructing the service and injecting it into the client is the injector. Injectors may also construct the clients passing in the dependencies on construction. It may connect complex object graphs. It does so by treating objects as clients and services. Services are themselves treated like clients if they have their own dependencies. It goes by many names: assembler, container, factory, builder, provider, main, etc. This role is not required by the Dependency Inversion Principle. This is often not something you have to build when implementing DI. Most frameworks proved ready-to-use injectors.

23:30 How It Works

Dependency Injection works by breaking the dependency between higher and lower level classes through interfaces. Higher level classes are any class that calls another to use it’s methods. These are your client objects. For example your controllers in ASP.NET are high level classes. Lower level classes are ones that are called by higher classes. These are the service objects. Data transfer or repository classes would be lower level classes. Any given class can by high and/or low based on it’s connection and dependencies to other classes.

Implementations of interfaces are passed into the client by the DI framework. The dependencies are created by the injector when calling the dependent code. The client is not allowed to create new or static methods. The client itself doesn’t need to know about the injecting code only the interfaces of the services being injected. It should not have any knowledge of the implementation of it’s dependencies. The client code should not have to change if the code behind the interface changes.

The injector creates the lowest dependency first and passes it into the next lowest. This continues until all dependency chains and hierarchies have been created. Then the injector calls the client passing in the dependencies.

Dependency injection is an alternative solution to the Service Locator Pattern. Like dependency injection, the service locator improves modularity and reduces dependencies between classes through interfaces. It differs from dependency injection in how it gets an implementation of the interface. Dependency injection has implementations provided through the constructor of a class. The service locator is a singleton registry for all services used in an application. This pattern requires a class to call the service registry when it needs an implementation of another class. While a service locator does reduce the dependency, being a singleton it makes it easy to create breaking changes in an interface implementation.

32:00 Basic Types of Dependency Injection

Constructor injection passes the dependencies into the client via a class constructor when constructing the client. This is the most common form of dependency injection. Dependencies are passed in as arguments for the constructor when creating the class. This is a special form of method injection that treats the constructor like a method that returns the class.

Method injection passes dependencies in as arguments of a method. This allows dependencies to be injected at the method invocation level. Dependencies are treated as arguments passed into the method.

Property injection stores the dependencies in properties of the instance after the class is constructed. Setter injection, an implementation of Property injection uses a public setter method on the client to set or inject the dependency. This is used for optional dependencies that are not required or have set defaults in the client.

Interface injection has the dependency provide an injector method that will inject the dependency into a client when passed into it. For this to work the client must have an interface with an exposed or public setter that accepts the dependency. It is used to tell the injector how to talk to the client.

Other types of frameworks exist for injecting dependencies. Some testing frameworks are not requiring clients to actively accept injection which makes testing legacy code possible. In Java it’s possible to use reflection to make private attributes public when testing. Some Inversion of Control implementations completely replace dependencies instead of removing them.

37:45 Advantages of Using A DI Framework

It makes testing individual units of code easier. Clients become more independent because the code is in testable chunks. It allows for mocks of dependencies to be created for testing.

Clients are able to be configurable. Clients can use anything that supports the interface the client expects. Only thing that is fixed is the behavior of the client. Implementation details are hidden so the client isn’t affected by changes or bug fixes.

There is less “boilerplate” code. Boilerplate code is parts of code that are reused multiple times but can’t be abstracted out. The injector initializes the dependencies.

Dependency Injection doesn’t require changes to the behavior of the code. It can be applied to legacy code. This improves testability and interaction between new and old code.

It allows for multiple developers working in the same code at the same time. Classes can use each other because they only need to know the interface. Plugins are a good example because they are developed by third parties that don’t interact with the original development team.

46:50 Disadvantages of Using A DI Framework

“The DI Why?”

Code can be difficult to read. The behavior is separated from construction. Increases the number of references in a system. Can be hard to follow as you need to find the implementation of the method called.

Frameworks tend to be implemented with reflection or dynamic programming. Makes it more difficult to use IDE tools used to find references. It also complicates refactoring as you have to trace paths that may not be obvious.

Dependency Injection increases the complexity of the code. More work is required up front to set up the dependencies and inject them instead of just creating what you need to call at the time it’s needed. Errors are not caught by linters and compliers because they now show up at run-time. It moves the complexity from the class level to the links between classes.

Creates a dependency on Dependency Injection frameworks. Once started you have to use the framework.

IoTease: Project

Hacking the IoT: Vulnerabilities and Prevention Methods

This is an article by Kaushik Pal, a technical architect and software consultant, about security in the IoT realm. Our world is becoming increasingly more connected. Just about anything you own can become a “smart” device. We have “smart” homes running multiple devices and even “smart” cities. In the article, Kaushik addresses some of the concerns with having so much connected to the internet. He talks about issues such as unsecured networks, weak interfaces, encryptions, out of date updates, and much more. For each one he explains the issue then provides potential solutions to working within the IoT realm.

Tricks of the Trade

Think about the systemic impacts of implementing best practices. If you can’t enforce them, if there are bad incentives, or if there isn’t team and managerial buy in, you’re going to have a bad time.

Editor’s Notes:

Will had the sniffles when recording this episode. We removed as many of them as possible.

Tagged with: , , , , , ,
2 comments on “Dependency Injection
  1. No, not “dependant”. It should be “dependent”.