S.O.L.I.D – 5. Zasada odwrócenia zależności (D – dependency inversion)

Ostatnia z zasad zbioru SOLID mówi o tym, że klasa wysokiego poziomu nie powinna zależeć od klasy niskiego poziomu. Obydwie powinny zależeć od abstrakcji. Znaczy to tyle, że w deklaracji żadnej klasy, metody czy zmiennej nie powinniśmy używać nazw klasy a tylko interfejsów lub klas abstrakcyjnych. Opiszmy to takim oto przykładem. Mamy warstwę programu odpowiedzialną za prezentowanie danych – klasę wysokiego poziomu (to z tą klasą użytkownik ma do czynienia), oraz, aby w ogóle było możliwe przetwarzanie i pokazywanie danych, warstwę dostępu do tych danych (klasa niskiego poziomu). Czyli ogólnie rzecz ujmując prezentowanie danych jest zależne od dostępu do danych. A zasada odwrócenia zależności odwraca tą logikę – niskopoziomowe klasy (moduły) podpinamy do interfejsów określonych przez moduł wysokiego poziomu (moduł ten decyduje o tym, jakie interfejsy będzie akceptował i tylko obiekty zgodne z tym interfejsem będzie można do niego podpiąć). Powyższa teoria rozjaśni się na przykładzie (kod PHP):

class User
{
}

class Email
{

     public function send()
     {
          //send email notification
     }

}

class NotificationManager
{

     public function sendNotification (User $user, $meddage)
     {
          $email = new Email();
          $email->send ($user, %message);
     }

}

Klasa Email to moduł niskopoziomowy odpowiadający stricte za wysyłanie powiadomień. Natomiast klasa NotificationManager to moduł wysokiego poziomu, ponieważ użytkownik z tym modułem będzie mieć do czynienia. Zobaczmy, że w powyższym przykładzie wiążemy metodę wysyłania powiadomień z menedżerem powiadomień (przez tworzenie obiektu email w klasie NotificationManager). Technicznie jest poprawne, ponieważ kod zadziałą, jednak problemem jest właśnie to powiązanie, silne sprzężenie.

class User
{
}

interface NotificationService
{

     public function send (User $user, $message);

}

class Email implements NotificationService
{

     public function send(User $user, $message)
     {
          //send email notification
     }

}

class NotificationManager
{

     public function sendNotification (User $user, NotificationService $notificationService, $meddage)
     {
          $notificationService->send ($user, $message);
     }

}

Dzięki powyższej zamianie odwróciliśmy wcześniejsze zależności i zyskaliśmy większą elastyczność. Jeżeli będziemy chcieli dodać inny rodzaj wysyłania powiadomień, to wystarczy dodać odpowiednią klasę (która implementuje stworzony interfejs) bez konieczności ingerencji w już istniejącą klasę NotificationManager.