Dependency Inversion czyli jak nie instalować lampy

Ostatnia, piąta litera w SOLID to Dependency Inversion. Dziś zastanowimy się czym jest w praktycznych zastosowaniach z życia.

Wyobraźmy sobie, że nie ma gniazdek elektrycznych. Kable biegną w ścianie. Tak samo urządzenia, takie jak lampa nie mają wtyczek tylko zwykły kabel zakończony dwoma żyłami. Żeby podłączyć lampę musimy wygrzebać przewód ze ściany i zedrzeć izolację. Potem bierzemy kabel lampy i dwa przewody zakręcamy bezpośrednio na tym, co wydostaliśmy ze ściany. Głupie prawda?

Niestety tak się programuje. Wewnątrz aplikacji często brak jakichkolwiek standardów. Brak wspólnych interfejsów. Wszystko jest mocno połączone ze sobą. Wygląda to tak jakbyśmy przylutowali lampę wprost do ściany.

W normalnym życiu mamy wspólne interfejsy. Gniazdka i wtyczki są właśnie tym. Możemy odłączyć lampę i przenieść ją w inne miejsce, do innego gniazdka. Oprócz lampy podłączymy też laptopa, radio czy mikrofalówkę.

Właśnie o tym mówi zasada odwracania zależności:

Moduły wysokiego poziomu (lampa) nie powinny zależeć od modułów niskiego poziomu (kabel). Oba powinny korzystać z abstrakcji (gniazdko).

Ale o co chodzi z tym odwróceniem?

Budujemy dom. Najpierw wylewamy fundamenty. Potem kładziemy podmurówkę. Podmurówka zależy od fundamentów. Potem stawiamy ściany, które zależą od podmurówki i fundamentów. Potem strop, który zależy od fundamentów, podmurówki i ścian itd. Najgorzej ma dach, który zależy od wszystkiego.

W budownictwie to ma sens. Grawitacji nie oszukasz. Musisz budować od podstaw. Wyjątki od tej reguły źle się kończyły jak np. wrocławski trzonolinowiec.

Inaczej jest w projektowaniu i inżynierii. Nie ma jednego kierunku i nic nie zabrania nam zaczęcia budowy od dymu z komina. Części produkuje się osobno a potem składa w całość. Takimi częściami są elementy programu – klasy, obiekty itp.

Niestety programowanie zbyt często porównuje się to budownictwa. Dawniej programiści budowali systemy warstwami. Najpierw najniższa warstwa, np. operacje na plikach, dysku, wejście i wyjście, klawiatura, ekran. Gdy to było gotowe patrzono co mamy i na tej podstawie budowano logikę biznesową.

Odwrócenie zależności to właśnie budowa od góry. To nie dobry kod jest celem aplikacji. Nie musimy implementować wszystkich możliwych operacji niskiego poziomu. Program musi być użyteczny. Musi spełniać logikę biznesową. Najpierw myślimy co chcemy. Potem programujemy logikę (albo projektujemy gniazdka i wtyczki). Dopiero na końcu tworzymy urządzenia i infrastrukturę, które dopasowują się do naszej logiki.

Projektowanie zaczynamy od interfejsów. Wspólnie opracujemy standard gniazdek i wtyczek. Gdy już do tego dojdziemy, różne zespoły programistów mogą niezależnie pracować nad swoimi fragmentami. Komunikują się tylko przez interfejsy.

W ten sposób żadne moduły systemu nie zależą od siebie. Wszystkie zależą od wspólnych abstrakcji.

Co nam daje Dependency Inversion?

  • Testowanie aplikacji jest łatwiejsze. Gdy wiemy co moduł, klasa, obiekt mają robić (mówi to interfejs) możemy łatwo napisać testy jednostkowe.
  • Równoległa praca nad elementami systemu jest możliwa. Programiści nie wchodzą sobie w drogę. Każdy zajmuje się swoją działką.
  • Przyspieszenie prac jest możliwe. Jeśli czas nas goni można łatwo zatrudnić więcej programistów. Nowy człowiek dostanie do ręki interfejs i zadanie aby go zaimplementować.
  • Zmiany w systemie są łatwe. Jeśli chcemy dodać nową funkcjonalność – wiemy gdzie. Ten sam interfejs implementujemy na nowy sposób, testujemy i gotowe. Reszta systemu się nie zmienia.

Zasadę odwróconej odpowiedzialności niektórzy mylą z wstrzykiwaniem zależności. Dependency inversion i dependency injection brzmią podobnie. Obie są sobie bliskie ale nie tożsame.

Wstrzykiwanie mówi aby obiekty nie organizowały sobie same potrzebnych komponentów tylko otrzymywały je z zewnątrz. Odwrócenie zależności mówi nam, aby nie wiązać elementów zbyt mocno ze sobą tylko polegać na abstrakcjach.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *