Ostatnią zasadą mnemoniku SOLID jest Odwrócenie Zależności. Polega ona na tym, żeby nie uzależniać modułów wysokiego poziomu od modułów niskiego poziomu. Należy wstawić pomiędzy nie abstrakcję, która nie może zależeć od detali, ale to detale powinny zależeć od niej. Prościej mówiąc zasada Odwrócenia Zależności ma na celu zmniejszenie zależności od konkretnych implementacji. W ten sposób kod nie będzie ulegał tak częstym zmianom (nawiązuje po części do zasady Open/Close Principle).
Klasyczny przypadek wzięty z filmów amerykańskich
1
2
3
4
5
6
7
8
public class Boss {
private OrganizationManager manager = new OrganizationManager();
public void orderManagerWork() {
manager.doStuff();
}
}
Klasa Boss jest modułem wysokiego poziomu, który zależy od modułu niskiego poziomu jakim jest OrganizationManager. Istnieje więc bezpośrednia zależność między nimi. Zmiana managera wiązałaby się z ingerencją w kod źródłowy. Naruszona została zasada Dependency Inversion Principle oraz Open/Close Principle. Z tego powodu należy wprowadzić abstrakcję.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Manager {
void doStuff();
}
public class Boss {
private Manager manager;
public Boss(Manager manager) {
this.manager = manager;
}
public void orderManagerWork() {
manager.doStuff();
}
}
W takiej postaci istnieje możliwość “wstrzyknięcia” konkretnej implementacji do klasy Boss. Dzięki temu istnieje luźne powiązanie pomiędzy nimi co znacznie upraszcza wszelkie modyfikacji. Możemy bez problemu przekazać szefowi odpowiedniego managera do danego zadania.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class OrganizationManager implements Manager {
@Override
public void doStuff() {
// do staff - organize meetings
}
}
public class MotivationManager implements Manager {
@Override
public void doStuff() {
// do staff - motivate team
}
}
W ten sposób zyskujemy bardzo wiele korzyści. Pierwszą podstawową jest to, że nasz kod jest łatwiejszy do testowania. Zamiast tworzyć wiele zaślepek możemy wprowadzić własną implementację interfejsu Manager, która ułatwi nam weryfikację działania klasy Boss. Dependency Inversion Principle jak dla mnie jest podstawą dla zasady Otwarte-Zamknięte. Dzięki niej nasz kod nie musi być modyfikowany. Możemy tylko utworzyć obiekt z inną implementacją klasy zależnej, aby otrzymać zmienione działanie. Nie musimy znać szczegółów implementacyjnych takiej klasy Boss. Po prostu przekazujemy wskazówki co do wykonania zadania, w postaci “wstrzykniętej” zależności, a resztą zajmuje się nasz wykonawca. Tak właśnie działają frameworki. Zasada Odwrócenia Zależności jest podstawą ich funkcjonowania!
Podsumowanie
Dzięki Dependency Inversion Principle nasz kod źródłowy jest łatwiejszy w utrzymaniu i jednocześnie dużo prostszy w testowaniu. Możemy budować funkcjonalności niczym z klocków. Gdy chcemy uzyskać inne działanie aplikacji jedyne co musimy zrobić to zmienić strategię jej wykonania poprzez podmianę zależności. Nie trzeba dokonywać żadnych zmian implementacyjnych.
Czy zgadzasz się z taką opinią? Może masz odmienne zdanie, którym chciałbyś się podzielić? Bądź też znasz inne przypadki użycia Dependency Inversion Principle? Podziel się swoimi uwagami w komentarzu bądź też napisz do mnie maila. Czekam z niecierpliwością na Twoją interesującą wypowiedź!