Ostatnia zasada akronimu SOLID, równie ważna jak pozostałe. Zmniejsza zależność między modułami, czym znacznie ułatwia rozszerzanie i modyfikowanie kodu.
D – Zasada odwracania zależności (Dependency Inversion Principle – DIP)
Definicja
Kod z warstw z wyższego poziomu nie powinien zależeć od tego z niższego poziomu. Za to obie wartstwy powinny zależeć od abstrakcji.
Z drugiej strony abstrakcje nie powinny zależeć od konkretnej implementacji, czyli szczegółów zawartych w obu warstwach.
Przykład
Stworzymy klasę/moduł wysokiego poziomu, która będzie odpowiedzialna za obsługę zamówienia i zapisywanie ich do bazy danych.
class OrderService {
private Database database;
public OrderService() {
this.database = new Database();
}
public void saveOrder(Order order) {
database.insert(order);
}
}
Następnie stworzymy klasę, reprezentującą moduł niskiego poziomu odpowiedzialną za dostęp do danych, która będzie wykonywać operacje wstawiania zamówien do bazy danych.
class Database {
public void insert(Object object) {
// logika dostępu do bazy danych
}
}
W powyższym przykładzie klasa OrderService narusza zasadę odrócenia zależności, ponieważ jest silnie związana z klasą Database. Jeżeli byłaby potrzeba zmiany sposobu dostępu do bazy danych trzeba by było zmodyfikować klasę OrderService i wszystkie inne klasy, które używają klasy Database.
W celu naprawienia powyższego kodu wystarczy dodać dodatkową warstwę abstrakcji pomiędzy modułami wysokiego i niskiego poziomu.
Następnie w klasie reprezentującej moduł wysokiego poziomu stworzymy pole, które będzie przechowywać zależność od abstrakcji i będziemy jej wymagać w konstruktorze tej klasy.
class OrderService {
private DataRepository dataRepository;
public OrderService(DataRepository dataRepository) {
this.dataRepository = dataRepository;
}
public void saveOrder(Order order) {
dataRepository.save(order);
}
}
Klasa odpowiadająca modułowi niskiego poziomu implementuje stworzony wcześniej interfejs.
class Database implements DataRepository {
public void save(Object object) {
// logika dostępu do bazy danych
}
}
Dzięki temu zabiegowi klasa OrderService nie zależy bezpośrednio od klasy Database, lecz od interfejsu DataRepository (który Database implementuje). Możliwe jest również łatwe podmienienie źródła danych, np. na plik. Wystarczy stworzyć nową klasę implementującą interfejs DataRepository i przekazać ją do konstruktora klasy OrderService.
Zależności można również wstrzykiwać przez właściwości lub parametry metod, nie tylko przez kontruktor.
[…] D – Zasada odwrócenia zależności (Dependency Inversion Principle – DIP) […]
[…] D – Zasada odwrócenia zależności (Dependency Inversion Principle – DIP) […]