Przejdź do treści

DevStyle - Strona Główna
Antywzrorzec Service Locator

Antywzrorzec Service Locator

Maciej Aniserowicz

11 lutego 2016

Backend

Wiecie jaka jest definicja wzorca projektowego, prawda? Za wikipedią: “a general repeatable solution to a commonly occurring problem in software design“. Czym zatem będzie antywzorzec? Czymś takim: “a general repeatable anti-solution to a commonly occurring problem in software design“. Czyli: recepta na napytanie sobie biedy. Czerwony pijany znak z napisem: “Nie idź tą drogą”.

Antywzorzec

Niektórzy będą wam mówić, że Service Locator to wzorzec. Że tak trzeba. I że rozwiązuje problemy. Widziałem takie stwierdzenia nawet na technicznych prezentacjach. Nie słuchajcie fałszywych proroków! A jeśli ktoś tak wam powie, to z wielką ostrożnością podchodźcie do wszelkich innych rekomendacji płynących z ich kłamliwych ust.

Service locator is the root of all dependency evil

W kontekście dependency injection, parafrazując słynne zdanie o optymalizacji: “service locator is the root of all evil“.

Poznajcie się…

Ale co to za ustrojstwo? Service Locator w połączeniu z dowolnym kontenerem Dependency Injection można zobrazować bardzo prosto. Polega to na udostępnieniu kontenera dla całej aplikacji! Każdy kawałek kodu może odwołać się bezpośrednio do niego, aby pobrać zależność, której aktualnie potrzebuje.

To takie “spimpowane new”. Gdybyśmy nie mieli kontenera, to musielibyśmy w różnych miejscach tworzyć nowe obiekty “ręcznie”. I ich zależności: też ręcznie. I zależności tych zależności: również ręcznie. Cały baobab (o którym to baobabie pisałem w poście “DI: kontener“). Ale masakra, prawda? I nagle objawienie: przecież mój obiekt “główny” może przyjąć kontener jako zależność i powyciągać sobie z niego wszystko to co chce!

Service Locator to spimpowane “new”. A operator “new” to też antywzorzec.

Posługując się “constructor injection”, czyli dostarczaniem zależności poprzez parametry konstruktora (najczęściej jedyne słuszne “injection”), deklaracja konstruktora ciągnie się i ciągnie. A to podobno źle, co nie? Ale nie da się inaczej, przecież ta moja klasa NAPRAWDĘ potrzebuje 15 zależności!

EUREKA! W takim razie te 15 zależności zastąpię jedną – kontenerem – i wszystko co mi potrzebne wyciągnę sobie z niego w trakcie pisania metod. Klasa będzie więc miała tylko jedną zależność. Zajebisty ze mnie modelarz! Pięknie, i niech się odczepią!

Prawdziwe oblicze

Jednak to oczywiście nie takie proste. Co nam da przekazanie kontenera w taki sposób? Patrząc realnie: absolutnie NIC. Prawie każda klasa będzie go potrzebowała, więc równie dobrze możemy wystawić go jako statyczne pole w statycznej klasie “DependencyMeneżer” i przestać udawać, że dbamy o wysoką jakość kodu.

clip_image001

Jeżeli będziemy korzystać z kontenera bezpośrednio, wszystkie zależności będą ukryte dokładnie tak samo jakbyśmy w ogóle olali Dependency Injection. Wówczas de facto to robimy: olewamy DI. Gdzie są zależności? W kontenerze. A gdzie jest wstrzykiwanie? Ano… nigdzie. Zamiast Dependency Injection uzyskujemy Dependency Hell.

Service Locator to jak zalepianie gnijącej rany szarą taśmą klejącą.

W takim przypadku tracimy wszystkie zalety płynące z DI. Zarządzanie czasem życia obiektów? Znika. Jawne deklarowanie zależności? Znika. Zasada Single Responsibility Principle, pięknie współgrająca z DI? Znika. Testowalność? Znika.

Tracimy zalety, ale wada pozostaje. Ta wada to sam fakt użycia kontenera, nauka “jak z niego korzystać” i dbanie o jego poprawną konfigurację.

To jak skasowanie testów, bo przeszkadzają w pisaniu kodu. To jak implementowanie security jedynie poprzez ukrywanie guzików na stronie. To jak jedzenie chipsów “zdrowych, prosto z pieca” na diecie. To jak zalepianie gnijącej rany szarą taśmą klejącą.

Don’t. Just don’t.

Po prawdziwie dobre praktyki związane z Dependency Injection odsyłam do niedawnego posta “DI: 3 calls pattern“.

Zobacz również