Zasada odwrócenia zależności

"Dochodzenie do mistrzostwa wymaga dwóch elementów: wiedzy i pracy." - Robert C. Martin

Zasada odwrócenia zależności

odwrócenia zależności

W myśl zasady odwrócenia zależności wszystkie zależności powinny w jak największym stopniu zależeć od abstrakcji, a nie od konkretnego typu. A więc klasa wysokiego poziomu nie powinna zależeć od klasy niskiego poziomu, ale obydwie klasy powinny zależeć od abstrakcji. Moduły (klasy) wysokiego poziomu najczęściej są odpowiedzialne za ważne decyzje strategiczne i modele danej aplikacji. Dlatego największym stopniu odpowiadają  one za funkcjonowanie całej aplikacji. Gdyby moduły wysokiego poziomu zależały od modułów niskiego poziomu, to zmiany elementów niskiego poziomu wymuszałaby również wprowadzenie zmian poziomów wyższych. Dodatkowo w momencie, gdy moduły wysokopoziomowe zależą od niskopoziomowych, ponowne ich wykorzystanie często jest utrudnione. Odwrócenie tej zależności w drugą stronę powoduje, że będzie można je wykorzystać wielokrotnie. Zatem kod źródłowy nie powinien być zależny od konkretnej klasy, zależności takie powinny kończyć się na klasach abstrakcyjnych lub interfejsach. Kod pisany w myśl zasady DIP jest dużo prostszy w konserwacji. Poniżej został przedstawiony kod napisany wbrew zasadzie DIP.

 
class Plane 
{ 
} 

class Engine 
{ 
public function startEngine() 
{ 
echo 'Uruchamiam silnik!'; 
} 
} 

class OnBoardComputer 
{ 
public function takeOff(Plane $plane) 
{ 
$start = new Engine(); 
$start -> startEngine($plane); 
} 
} 

$plane = new OnBoardComputer(); 
$plane -> takeOff(new Plane()); 

Moduł wysokiego poziomu, jakim jest klasa OnBoardComputer jest uzależniony od modułu niskiego poziomu – klasa Engine. Oznacza to, że w momencie rozbudowy aplikacji, nie jest możliwe (bez wprowadzania zmian w klasie OnBoardComputer) dodanie kolejnej klasy. Taką klasą mogłaby być np. klasa odpowiedzialna za chowanie kół w podwoziu (ten element również występuje podczas startu samolotu!). Rozwiązaniem tego problemu jest wprowadzenie interfejsu implementowanego przez klasę niższego poziomu – Engine. Interfejs ten spowoduje, że każda klasa implementująca go będzie musiała zaimplementować metodę startEngine(). Zakładamy, że bez uruchomienia silnika nie da się, np. schować kół podwozia ;). Na poziomie klasy OnBoarComputer istnieje zależność od interfejsu (od obiektu zgodnego z interfejsem OnBoarComputerService). Poniżej znajduje się kod źródłowy zgodny z zasadą DIP:

class Plane
{
}

interface OnBoardComputerService
{
public function startEngine(Plane $plane);
}

class Engine implements OnBoardComputerService
{
public function startEngine(Plane $plane)
{
echo 'Uruchamiam silnik!';
}
}

class OnBoardComputer
{
public function takeOff(Plane $plane, OnBoardComputerService $onBoardComputerService)
{
$onBoardComputerService -> startEngine($plane);
}
}
$plane = new OnBoardComputer();
$plane -> takeOff(new Plane(), new Engine());