
„High-level modules should not depend on low-level modules. Both should depend on abstractions.”
„Abstractions should not depend on details. Details should depend on abstractions.”
— Robert C. Martin
O co chodzi z DIP?
Zasada DIP mówi: moduły wysokiego poziomu (np. logika biznesowa) nie powinny być zależne od modułów niskiego poziomu (np. zapis do pliku, bazy danych, wysyłka maili). Oba powinny zależeć od abstrakcji.
Innymi słowy: jeśli kod „główny” zna konkretne klasy pomocnicze, to jest z nimi zbyt silnie związany. Gdy chcesz coś zmienić, np. inny system logowania – musisz zmieniać też logikę aplikacji. A to zły znak.
Przykład łamania DIP – konkretna zależność
Załóżmy, że mamy klasę Powiadamiacz, która wysyła maile:
public class Powiadamiacz {
private EmailSerwis emailSerwis = new EmailSerwis();
public void wyslijPowiadomienie(String tresc) {
emailSerwis.wyslijEmail(tresc);
}
}
public class EmailSerwis {
public void wyslijEmail(String tresc) {
System.out.println("Wysyłam email: " + tresc);
}
}
Problem? Powiadamiacz zna konkretną klasę EmailSerwis. Jeśli chcesz wysyłać SMS-y, Slacka czy notyfikacje push – musisz przerabiać Powiadamiacz. Kod jest sztywny.
Przestrzeganie DIP – zależność od interfejsu
Najpierw tworzymy abstrakcję – interfejs:
public interface NadawcaPowiadomien {
void wyslij(String tresc);
}
Implementacje:
public class EmailSerwis implements NadawcaPowiadomien {
public void wyslij(String tresc) {
System.out.println("Email: " + tresc);
}
}
public class SmsSerwis implements NadawcaPowiadomien {
public void wyslij(String tresc) {
System.out.println("SMS: " + tresc);
}
}
I teraz Powiadamiacz korzysta z interfejsu:
public class Powiadamiacz {
private NadawcaPowiadomien nadawca;
public Powiadamiacz(NadawcaPowiadomien nadawca) {
this.nadawca = nadawca;
}
public void wyslijPowiadomienie(String tresc) {
nadawca.wyslij(tresc);
}
}
Użycie:
Powiadamiacz mailowy = new Powiadamiacz(new EmailSerwis());
mailowy.wyslijPowiadomienie("Zamówienie zostało wysłane");
Powiadamiacz smsowy = new Powiadamiacz(new SmsSerwis());
smsowy.wyslijPowiadomienie("Twoja paczka jest już w drodze");
Jak to się robi w praktyce?
W praktyce zasada Dependency Inversion jest realizowana poprzez przekazywanie zależności do klasy z zewnątrz, zazwyczaj przez konstruktor lub metodę ustawiającą (setter). Dzięki temu klasa nie tworzy obiektów pomocniczych samodzielnie, lecz otrzymuje je jako gotowe implementacje interfejsów. To pozwala łatwo podmieniać konkretne rozwiązania – np. w testach zamiast prawdziwego serwisu możesz przekazać atrapę (mock).
Korzyści z DIP
- Większa elastyczność – zmieniasz implementacje bez grzebania w logice,
- Łatwiejsze testowanie – możesz wstrzyknąć klasę testową (mock),
- Niższe sprzężenie – klasy mniej od siebie zależą,
- Większa rozszerzalność – dokładanie nowych form powiadomień nie wymaga zmian w istniejących klasach.
Podsumowanie
Dependency Inversion to zasada, która uwalnia Twój kod. Zamiast betonować zależności, tworzysz abstrakcyjne punkty styku. Klasa nie powinna wiedzieć kto coś robi – wystarczy, że wie co ma być zrobione.
Kod staje się jak gniazdko – możesz do niego podłączyć różne wtyczki. I o to chodzi w czystej architekturze.

Dodaj komentarz