Dependency Inversion Principle

High level modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.

So what does dat mean. High level module. A high level module is in fact a class in which other classes is used. Therefore the other classes needs to be instantiated. That's the issue. Example

    public class InvoiceService
    {
        public void CreateInvoices()
        {
            var invoiceRepository = new InvoiceRepository();
            var allOpenInvoices = invoiceRepository.GetAllOpen();
        }
    }

In this case the higher level module is the InvoiceService and the lower level module is the InvoiceRepository. There is a dependency from the InvoiceService on the InvoiceRepository because it needs that to get all the open invoices. It needs to be instantiated. In order to remove the instantiation of the invoiceRepository we can use depency injection.

    public class InvoiceService
    {
        private readonly InvoiceRepository _invoiceRepository;
        
        public InvoiceService(InvoiceRepository invoiceRepository)
        {
            _invoiceRepository = invoiceRepository;
        }
        public void CreateInvoices()
        {
            var invoiceRepository = new InvoiceRepository();
            var allOpenInvoices = _invoiceRepository.GetAllOpen();
        }
    }

So now we ask the dependency injection to insert the InvoiceRepository in the constructor. Like this we don't have to 'new' the repository and we have removed that dependency.

Both should depend on abstractions

Abstraction is the process of hiding certain details and only showing certain information to the other modules. In C# this can be done with a abstract class but most of the time we talk about interfaces. In order to comply this criteria with the earlier example we need to implement interfaces:

    public class InvoiceService
    {
        private readonly IInvoiceRepository _invoiceRepository;
        public InvoiceService(IInvoiceRepository invoiceRepository)
        {
            _invoiceRepository = invoiceRepository;
        }
        public void CreateInvoices()
        {
            var invoiceRepository = new InvoiceRepository();
            var allOpenInvoices = _invoiceRepository.GetAllOpen();
        }
    }

So we have created a interface: IInvoiceRepository which has a method GetAllOpenInvoices. The dependency injection module is going to insert the class which is registered with that interface. Like that the hard coupling between InvoiceService and InvoiceRepository is gone. 

Abstractions should not depend on details. Details should depend upon abstractions

We have seen earlier that most of the time an abstraction is a interface. Well abstraction should not depend on details means the code you are going to write should not go in the interface.

    public interface IInvoiceRepository
    {
        IEnumerable GetAllOpen();
    }

So the abstraction, in this case the interace doesn't have any code. The code to get all the open invoices should go in the repository class itself. Before C# 8 we could not even do that. The runtime was giving a error if you wrote an implementation in the interface. Since C#8.0C#8 interface implement code we can write an implementation in an interface. So we need te be carefull with this otherwise we violate the Dependency Inversion Principle.

Tools to handle the Dependency Inversion Principle

There are several solutions ot handle the Dependency Inversion Principle. The most used one is the Dependency Injection. And even since this is build in .net Core it's the most likely used solution. But a factory pattern is allso a solution!
- Dependency Injection Dependency Injection
- Factory Pattern Factory pattern

{{opmerking.Naam}}:

{{opmerking.OpmerkingText}}

            

Saving your comment....

Naam is verplicht!
Email is verplicht!
Opmerking is verplicht!