📖 Business
DI Patterns Catalog
Van Deursen and Seemann identify three canonical patterns for supplying dependencies to a class, each suited to different design scenarios. Constructor Injection is the default and most common — dependencies are provided through the class constructor and stored as read-only fields, ensuring the object is always in a valid state. Method Injection passes a dependency as a parameter to a specific method call, useful when the dependency varies per operation or isn't known at construction time. Property Injection assigns a dependency through a writable property after construction, reserved for cases where a sensible default exists and overriding is optional. Together, these three patterns form the complete vocabulary for how dependencies flow into a class.
2
Minutes
2
Concepts
+45
XP
1
How It Works
- Constructor Injection (the default) — Dependencies are declared as constructor parameters, stored in
private readonlyfields, and guaranteed non-null via guard clauses. This makes the class's requirements explicit, enforces immutability, and ensures the object cannot be created in an invalid state. Use this for every required dependency.
csharp
public class OrderService
{
private readonly IOrderRepository _repo;
private readonly ILogger _logger;
public OrderService(IOrderRepository repo, ILogger logger)
{
_repo = repo ?? throw new ArgumentNullException(nameof(repo));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
}
- Method Injection (per-call variation) — A dependency is supplied as a method parameter rather than a constructor parameter. This is appropriate when the dependency changes with each call or when the caller naturally possesses the dependency. Common in middleware pipelines and strategy patterns.
csharp
public void Process(Order order, IDiscountStrategy strategy)
{
var discount = strategy.Calculate(order);
}
- Property Injection (optional, with default) — A dependency is set via a public property, typically with a default implementation already assigned. This is the rarest legitimate pattern, used only when a reasonable default exists and most consumers won't need to override it. Logging is the classic example.
- Constructor Injection as a forcing function — When a constructor accumulates too many parameters (typically more than four), it signals a Single Responsibility Principle violation. The pattern makes design smells visible — you can't hide a god class when its constructor demands twelve dependencies.
- Choosing the right pattern — Decision tree:
- Is the dependency required for the class to function? → Constructor Injection
- Does the dependency vary per method call? → Method Injection
- Is the dependency optional with a sensible default? → Property Injection