Dependency inversion principle — Zasada odwróconej zależności
Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych, lecz zależność powinna wynikać z abstrakcji.
Jak wykorzystywać w praktyce DIP?
Twórzmy dużo abstrakcyjnych bytów. Każda klasa niech implementuje interfejs lub dziedziczy po klasie abstrakcyjnej. Tworząc oprogramowanie, możemy najpierw stworzyć tylko interfejsy, a dopiero później je zaimplementować.
Pomocne w spełnieniu tej zasady jest przestrzeganie tych reguł:
- Każda zmienna w klasie jest referencja do abstrakcji.
- Wszystkie klasy dziedziczą po abstrakcji.
- Żadna klasy potomna nie przesłania metod z klasy bazowej.
- Ustawianie zmiennych realizowane jest przez wzorzec fabryki lub wstrzykiwania zależności.
Przykład
Poniżej mamy klasę pokazującą raport. Dane do raportu brane są za bazy danych. Implementacja wygląda następująco:
class Program
{
static void Main(string[] args)
{
var reportImporter = new ReportDatabaseImporter();
var reportPresenter = new ReportPresenter(reportImporter);
reportPresenter.ShowReport();
}
}
internal class ReportPresenter
{
private readonly ReportDatabaseImporter importer;
public ReportPresenter(ReportDatabaseImporter importer)
{
this.importer = importer;
}
public void ShowReport()
{
Console.WriteLine(importer.GetReportData());
}
}
internal class ReportDatabaseImporter
{
public string GetReportData()
{
return "Report from Database";
}
}
Po jakimś czasie dostaliśmy prośbę o wyświetlanie danych również z pliku Excel w taki sam sposób. Niestety, nie jesteśmy w stanie zrobić tego łatwo. Musimy zmienić klasę prezentującą raport. Takie rozwiązanie pozwala nam dodawać łatwiej kolejne źródła raportu:
using System;
namespace SRP.Bad
{
class Program
{
static void Main(string[] args)
{
var reportImporter = new ReportDatabaseImporter();
var reportPresenter = new ReportPresenter(reportImporter);
reportPresenter.ShowReport();
var reportImporter2 = new ReportExcelImporter();
var reportPresenter2 = new ReportPresenter(reportImporter2);
reportPresenter2.ShowReport();
}
}
internal class ReportPresenter : IReportPresenter
{
private readonly IReportImporter importer;
public ReportPresenter(IReportImporter importer)
{
this.importer = importer;
}
public void ShowReport()
{
var reportData = importer.GetReportData();
Console.WriteLine(reportData.GetContent());
}
}
internal class ReportDatabaseImporter : IReportImporter
{
public IReportData GetReportData()
{
return new ReportData("Report from Database");
}
}
internal class ReportExcelImporter : IReportImporter
{
public IReportData GetReportData()
{
return new ReportData("Report from Excel");
}
}
internal class ReportData : IReportData
{
private readonly string content;
public ReportData(string content)
{
this.content = content;
}
public string GetContent()
{
return content;
}
}
internal interface IReportData
{
string GetContent();
}
internal interface IReportImporter
{
IReportData GetReportData();
}
internal interface IReportPresenter
{
void ShowReport();
}
}
Mamy tutaj dużą liczbę interfejsów. Ułatwiają one jednak rozwój aplikacji oraz testowanie. W testach będziemy mogli pod interfejs podstawić mocka.
Wszystkie posty związane z mini projektem: Poznaj zasady SOLID i OOP:
- Początek mini projektu: Poznaj zasady SOLID i OOP
- SOLID
- S jak Single responsibility principle, czyli zasada jednej odpowiedzialności
- O jak Open-closed principle, czyli zasada otwarte/zamknięte
- L jak Liskov Substitution Principle, czyli zasada podstawień Barbary Liskov
- I jak Interface segregation principle, czyli Zasada segregacji interfejsów
- D jak Dependency Inversion Principle, czyli Zasada odwrócenia zależności
- CS jak Ćwiczenia Single responsibility principle, czyli zasada jednej odpowiedzialności
- CO jak Ćwiczenia Open/closed principle, czyli Zasada otwarte-zamknięte
- CL jak Ćwiczenia Liskov Substitution Principle, czyli zasada podstawień Barbary Liskov
- CI jak Ćwiczenia Interface segregation principle, czyli Zasady segregacji interfejsów
- CI jak Ćwiczenia Dependency Inversion Principle, czyli Zasada odwrócenia zależności
- Ćwiczenia z SOLID
- Podsumowanie połowy projektu: Poznaj zasady SOLID i OOP
- Ćwiczenia z SOLID — Kata
- OOP — Object Oriented Programming, czyli programowanie obiektowe
- OOP — Myślenie obiektowe
- OOP — Object Oriented Programming, czyli programowanie obiektowe — Modelowanie dziedziny
- KISS — Keep it simple, stupid, czyli Bez udziwnień zapisu, idioto (BUZI)
- Lod — Law of Demeter, czyli Prawo Demeter
- DRY — Don’t repeat yourself, czyli Nie powtarzaj się
- SLAP — Single Level of Abstraction Principle, czyli Pojedynczy poziom abstrakcji
- Composition Over Inheritance, czyli Kompozycja ponad dziedziczeniem
- Encapsulate what changes, czyli Ukrywaj zmieniające się rzeczy
- Podsumowanie projektu: Poznaj zasady SOLID i OOP
- Podsumowanie zasad SOLID i OOP
- Mini kurs: Poznaj zasady SOLID i OOP – Zapisz się!
- Praca cząstkowa w Metodzie Kanban
Źródła
Obraz główny
Materiały
- Czysta architektura — Robert C. Martin
- https://code.tutsplus.com/tutorials/solid-part-4-the-dependency-inversion-principle–net-36872
- https://lukaszkosiorowski.pl/programowanie/solid-zasada-odwracania-zaleznosci/
- http://tomaszjarzynski.pl/solid-czesc-5-zasada-odwrocenia-zaleznosci/
- http://devman.pl/pl/techniki/zasady-solid-5-zasada-odwrocenia-zaleznoscidependency-inversion/
- https://www.modestprogrammer.pl/solid-interface-segregation-principle-isp-wszystko-co-powinienes-wiedziec-o-zasadzie-segregacji-interfejsow