From Dependency Injection, Principles, Practices, and Patterns by Steven van Deursen and Mark Seemann

This article delves into the PROPERTY INJECTION DI pattern—what it is and how, when, and why to use it.


Take 37% off Dependency Injection, Principles, Practices, and Patterns. Just enter code fccseemann into the discount code box at checkout at manning.com.


How do we enable DI as an option in a class when we have a good Local Default?

By exposing a writable property that lets callers supply a DEPENDENCY if they want to override the default behavior.

Listing 1.  Injecting a DEPENDENCY using PROPERTY INJECTION

 
 public class Consumer
 {
     public IDependency Dependency { get; set; } 
         = new DefaultImplementation();          
  
     public void DoSomething()                   
     {
         this.Dependency.DoStuff();
     }
 }
 

Public property allows setting a particular Dependency.

Property gets initialized with a Local Default.

Method that uses the Dependency.

When you’re developing a class that has a DEPENDENCY, you probably have a particular implementation of that DEPENDENCY in mind. If you’re writing a domain service that accesses a Repository, you’re most likely planning to develop an implementation of a Repository that uses a relational database.

It would be tempting to make that implementation the default used by the class under development. But when such a prospective default is implemented in a different assembly, using it as a default means creating a hard reference to that other assembly, effectively violating many of the benefits of loose coupling. Such implementation is a FOREIGN DEFAULT. A class that has a hard reference to a FOREIGN DEFAULT causes tight coupling.

Conversely, if the intended default implementation is defined in the same library as the consuming class, you won’t have that problem. This is unlikely to be the case with Repositories, but such LOCAL DEFAULTS often arise as implementations of the Strategy pattern.

When a class has a good LOCAL DEFAULT, but you still want to leave it open for extensibility, you can expose a writable property that allows a client to supply a different implementation of the class’s DEPENDENCY than the default, as demonstrated in figure 1.

PROPERTY INJECTION should only be used when the class you’re developing has a good LOCAL DEFAULT, and you still want to enable callers to provide different implementations of the class’s DEPENDENCY. It’s important to note that PROPERTY INJECTION is best used when the DEPENDENCY is optional. If the DEPENDENCY is required, Constructor Injection is always a better pick.

The main advantage of PROPERTY INJECTION is that it’s easy to understand. We’ve often seen this pattern used as a first attempt when people decide to adopt DI.

Appearances can be deceptive, though, and PROPERTY INJECTION is fraught with difficulties. It’s challenging to implement it in a robust manner. Clients can forget to supply the DEPENDENCY, a code smell known as Temporal Coupling. Additionally, what would happen if a client tries to change the DEPENDENCY in the middle of the class’s lifetime? This could lead to inconsistent or unexpected behavior, so you may want to protect yourself against that event.

Despite the downsides, it makes sense to use PROPERTY INJECTION when building a reusable library. It allows components to define sensible defaults, and this simplifies working with a library’s API. When building applications, on the other hand, PROPERTY INJECTION should only be used sparingly. Even though you might have a LOCAL DEFAULT for a DEPENDENCY, CONSTRUCTOR INJECTION still provides you with a better alternative. CONSTRUCTOR INJECTION is simpler and more robust.

When developing applications, you wire up your classes in your Composition Root. CONSTRUCTOR INJECTION prevents you from forgetting to supply the DEPENDENCY. Even in the case that there’s a LOCAL DEFAULT, such instances can be supplied to the constructor by the COMPOSITION ROOT. This simplifies the class and allows the COMPOSITION ROOT to be in control over the value that all consumers get. This might even be a Null Object implementation.

It is tempting to apply PROPERTY INJECTION as a solution to Constructor Over-injection. Classes with many DEPENDENCIES, however, are a code smell and PROPERTY INJECTION won’t lower the class’s complexity. More effective patterns exist that do lower the class’s complexity exist.

If you want to learn more about the book, check it out on livebook here.

Check out more excerpts from Dependency Injection, Principles, Practices, and Patterns:

Writing Maintainable, Loosely-Coupled Code
Understanding Method Injection
Understanding the Composition Root
Understanding Constructor Injection
Abuse of Abstract Factories
The Service Locator Anti-Pattern
The Ambient Context Anti-Pattern
The Transient Lifestyle
The Scoped Lifestyle
The Singleton Lifestyle