Understanding Constructor Injection
From Dependency Injection, Principles, Practices, and Patterns by Steven van Deursen and Mark Seemann
Take 37% off Dependency Injection, Principles, Practices, and Patterns. Just enter code fccseemann into the discount code box at checkout at manning.com.
This article delves into the Constructor Injection DI pattern — what it is and how, when, and why to use it.
How do we guarantee that a necessary dependency is always available to the class we’re currently developing?
By requiring all callers to supply the Dependency as a parameter to the class’s constructor.
When a class requires an instance of a Dependency, you can supply that Dependency through the class’s constructor, enabling it to store the reference for future use.
Constructor Injection is the act of statically defining the list of required Dependencies by specifying them as parameters to the class’s constructor.
The constructor signature is compiled with the type and it’s available for all to see. It clearly documents that the class requires the Dependencies it requests through its constructor.
The class that needs the Dependency must expose a public constructor that takes an instance of the required Dependency as a constructor argument. This should be the only publicly available constructor. If more than one Dependency is needed, additional constructor arguments can be added to the same constructor. Listing 1 shows the definition of a consuming
HomeController class that needs an instance of the
IProductService Dependency to work.
Listing 1 Injecting a Dependency using Constructor Injection
public class HomeController
private readonly IProductService service; ❶
public HomeController( ❷
IProductService service) ❸
if (service == null) ❹
throw new ArgumentNullException("service"); ❹
this.service = service; ❺
❶ Private instance field to store supplied Dependency.
❷ Constructor that statically defines its Dependencies.
❸ Argument for supplying the required Dependency.
❹ Guard Clause to prevent clients from passing in null.
❺ Storing the Dependency in the private field for later use. The constructor contains no other logic than verifying and storing its incoming Dependencies.
IProductService Dependency is a required constructor argument of
HomeController; any client that doesn’t supply an instance of
IProductService can’t compile, but because an interface is a reference type, a caller can pass in
null as an argument to make the calling code compile. You need to protect the class against such misuse with a Guard Clause. Because the combined efforts of the compiler and the Guard Clause guarantee that the constructor argument is valid if no exception is thrown, the constructor can store the Dependency for future use without knowing anything about the real implementation.
Keep the constructor free of any other logic to prevent it from performing any work on Dependencies. The Single Responsibility Principle implies that members should do only one thing. Now that you’re using the constructor to inject Dependencies, you should keep it free of other concerns. This makes the construction of your classes fast and reliable.
When the constructor has returned, the new instance of the class is in a consistent state with a proper instance of its Dependency injected into it. Because the constructed class holds a reference to this Dependency, it can use the Dependency as often as necessary from any of its other members. Its members don’t need to test for
null, because the instance is guaranteed to be present.
Constructor Injection should be your default choice for DI. It addresses the most common scenario where a class requires one or more Dependencies.It guarantees that the Dependency must be provided. If the depending class can’t function without the Dependency, such a guarantee’s valuable. Table 1 provides a summary of the advantages and disadvantages of Constructor Injection.
Table 1 Constructor Injection advantages and disadvantages
The main disadvantage to Constructor Injection is that if the class you’re building is called by your current application framework, you might need to customize that framework to support it. Some frameworks assume that your classes will have a parameterless constructor. (This is called the Constrained Construction anti-pattern.) In this case, the framework will need special help creating instances when a parameterless constructor isn’t available.
An apparent disadvantage of Constructor Injection is that it requires that the entire Dependency graph be initialized immediately. Although this sounds inefficient, it’s rarely an issue. After all, even for a complex object graph, we’re typically talking about creating a few dozen new object instances, and creating an object instance is something that most platforms and runtimes do extremely fast. Any performance bottleneck your application has will appear in other places. As previously stated, component constructors should be free from all logic except guard checks and storing incoming Dependencies. This makes construction fast and prevents most performance issues.
Constructor Injection is the most generally applicable DI pattern available, and also the easiest to implement correctly. It applies when the Dependency is required.
If you want to learn more about the book, check it out on livebook here.
About the authors:
Mark Seemann is a programmer, software architect, and speaker who has been working with software since 1995, including six years with Microsoft. Steven van Deursen is a seasoned .NET developer and architect, and the author and maintainer of the Simple Injector DI library.
Originally published at freecontent.manning.com.