This post is part of the series SOLID Wash Tunnel.
Definition
"Dependency injection makes a class independent of its dependencies. It achieves that by decoupling the usage of an object from its creation."
- Stackify
Dependency injection is used to implement the Dependency Inversion and Inversion of Control principles. It involves 3 types of players:
- Dependent - A class which depends on some
dependency. - Dependency - A class which offers some kind of service. This is often served via an
abstractionto decouple thedependentfrom a concrete implementation of thedependency. - Injector - A class/component/framework which injects a
dependency, into thedependent.
Our custom IoC Container is considered to be aninjector.

Types of DI
As stated above the injector injects the dependency into the dependent class. This can be achived in 3 ways:
- Constructor Injection - The
injectorsupplies thedependencythrough thedependentclass constructor. - Method Injection - The
injectorsupplies thedependencythrough any method of thedependentclass, which accepts saiddependency/abstraction. - Property Injection - The
injectorsupplies thedependencythrough a public property of thedependentclass.
Our custom IoC Container only supports constructor injection, so we will be using that throughtout the project.
Implementation
Dependency Injection is used across the whole code base!
For example lets elaborate the individual parts the PriceCalculator class, and relate them to the definitions made above for DI.
- PriceCalculator - Is the
dependentsince it needs theICurrencyRateConverterto do its work. - ICurrencyRateConverter - Is the
dependencyor more precisely theabstractionthat is injected via constructor injection. It offers rate conversion functionality that thePriceCalculatordepends on. - Our IoC container (Not shown here) - Is the
injectorsince it supplies thePriceCalculatorwith an instance ofICurrencyRateConverter.
public interface IPriceCalculator
{
int Discount { get; }
Money Calculate(IWashProgram program, Currency currency);
}
public abstract class PriceCalculator : IPriceCalculator
{
protected readonly ICurrencyRateConverter converter;
public PriceCalculator(ICurrencyRateConverter converter)
{
this.converter = converter;
}
public virtual Money Calculate(IWashProgram program, Currency currency)
{
Money totalPrice = program
.GetWashSteps()
.Select(x => x.Price)
.Aggregate((x, y) => x + y);
return converter.Convert(totalPrice, currency);
}
}
public class IndividualPriceCalculator : PriceCalculator
{
public override int Discount => 0;
public IndividualPriceCalculator(ICurrencyRateConverter converter)
: base(converter) { }
}
public class CompanyPriceCalculator : PriceCalculator
{
public override int Discount => 20;
public CompanyPriceCalculator(ICurrencyRateConverter converter)
: base(converter) { }
public override Money Calculate(IWashProgram program, Currency currency)
{
Money totalPrice = base.Calculate(program, currency);
return totalPrice - (Discount / 100m * totalPrice);
}
}Principles
| Principle | Applied | Explanation |
|---|---|---|
| SRP | ✅ | Deals only with calculation of prices. |
| OCP | ✅ | Different calculations are performed by extending the base PriceCalculator class. |
| LSP | ✅ | You can supply an IndividualPriceCalculator or CompanyPriceCalculator where a PriceCalculator is expected. |
| ISP | ✅ | PriceCalculator (the client) makes use of all the methods of IPriceCalculator. |
| DIP | ✅ | High level modules like PriceCalculator do not depend on the low level modules like CurrencyRateConverter, LegacyCurrencyRateConverter. They depended on the abstraction ICurrencyRateConverter. |
Continue the series on Simple Factory.
If you found this article helpful please give it a share in your favorite forums 😉.
The solution project is available at GitHub.

