Robienie interfejsów do serwisów to jedyny natywny sposób umożliwiający ich mockowanie w testach, bez tego trzeba polegać na 3rd party frameworkach typu mockito.
No na pewno nie jedyny.
Poza tym jest zgodne z zasadami solid, zwiększa elastyczność kodu, wprowadza dependency inversion i luzuje zależności między kodem, interfejsy można wydzielić w osobną bibliotekę żeby uniknąć cyklicznych zależności.
Samo dodanie interfejsu nie oznacza jeszcze Dependency Inversion. Interfejs też może być "ubrudzony" szczegółami implementacyjnymi, i wtedy lipa z takiego Dependency Inversion.
Witam
mam pytanie to reszty bardziej doświadczonych kolegów z forum. Jaką macie opinie o Service I ServiceImpl - mowa tylko o wyłącznie Servisie, który implementuje jeden interfejs.
Wspomniany interfejs nie jest implementowany przez inną klasę.
Są jakiekolwiek zalety takiego rozwiązania?
Szukałem na internecie i niestety nie udało mi się znaleźć odpowiedzi. Opinie były podzielone, wręcz co dziwne to liczba głosów rozkłada się prawie na
50% tak, 50% nie.
Ja osobiście nie do końca rozumiem sens, aby tak robić, a wy jakie rozwiązania stosujecie u siebie?
Byłby ktoś w stanie jakoś uzasadnić takie rozwiązanie?
// Edit
Chciałbym tylko dodać, że mówimy tylko o klasach, które nie będą w żaden sposób rozszerzane
Interfejs będzie implementowany tylko zawsze przez jedną klasę
Sama zasadność dodania interfejsów do "warstwy" serwisów, celem oddzielenia ich od np biblioteki HTTP albo od bazy nawet ma sens. Tylko to ma sens wtedy, kiedy faktycznie chcesz oddzielić te warstwy od siebie - w taki sposób, że mógłbyś jedną z nich usunąć, i druga nadal by działała (z minimalnym postawieniem). Wtedy tak.
Tylko że w "zwykłych" aplikacjach webowych nikt praktycznie tak nie robi - dodaje się wtedy interfejs pomiędzy dwie warstwy, ale tak na prawdę ten interfejs nadal jest "ubrudzony" szczegółami implementacyjnymi tego serwis, więc o żadnym oddzieleniu nie ma mowy. Ludzie dodają taki pół środek (nawet nie, ćwierć środek), w stylu "dodamy interfejs, żeby mieć DI" - "ale to nadal nie pozwoli dobrze podmienić implementacji" - "aaaj tam". Więc często w takich zespołach nie można usunąć tego nadmiarowe interfejsu, bo ludzie mówią "zostaw, chcemy mieć DI"; ale nie można tego go "wyczyścić" ze szczegółów implementacyjnych, bo wtedy ludzie znowu mówią "aaa, nie potrzeba".
Prawda jest taka, że żeby dobrze oddzielić dwie warstwy od siebie, to nie jest łatwe - trzeba umieć to robić i widzieć co i po co się to robi. To nie jest takie proste jak "użyj interfejs tutaj zamiast klasy". Tylko jak mówiłem - to nie jest łatwe, i nie jest takie 0/1kowe żeby to dodać. Geneza tego podejścia najczęściej jest taka, że ktoś gdzieś usłyszał że "interfejsy są dobre", więc je dodał - ale nie do końca kuma zasadność ich działania tutaj.
Także jeśli mowa jest o takiej "zwykłej" apce, to Service/ServiceImpl najpewniej nie ma sensu.
PS: To jest bardzo częsty problem w laravelu w PHP - jest taka konwencja ze repozytoria mają mieć impl oraz interfejs - tylko że te interfejsy, i tak w zwracanym typie mają DTO z bazy. Więc o żadnym Dependency Inversion nie może być mowy w takim czymś.
tl;dr; Interfejsy z jedną implementacją mają sens, jak chcesz oddzielić dwie zależności od siebie. Ale kiedy nie są oddzielone (np ciągle jest szczegól implementacyjny w interfejsie), to wtedy IMO nie mają sensu.
A odpowiadając na pozostały post od @obscurity
Poza tym jest zgodne z zasadami solid, zwiększa elastyczność kodu, wprowadza dependency inversion i luzuje zależności między kodem, interfejsy można wydzielić w osobną bibliotekę żeby uniknąć cyklicznych zależności.
jest zgodne z zasadami solid, zwiększa elastyczność kodu, wprowadza dependency inversion i luzuje zależności między kodem
- Tak, ale tylko jeśli się to wprowadzi odpowiednio (czyli interfejs nie ma szczegółów implementacynych), a w 95% przypadków nie jest wprowadzone odpowiednio.
To jest jedyna dobra rada której udzieliłeś w tym poście.
Jeśli będziesz chciał na przykład kiedyś owrappować ServiceImpl w powiedzmy warstwę cache'ującą możesz to zrobić w jednym miejscu zamieniając tylko reguły wstrzykiwania zależności zamiast poprawiać kod w wielu miejscach lub komplikując kod serwisu wprowadzając cache w nim samym.
A o YAGNI Pan słyszał? Nie robi się interfejsów teraz, po to żeby BYĆ MOŻE w przyszłości dodać cache. Nie przewidzisz wszystkich przypadków, tego co trzeba będzie zrobić w przyszłości, więc bez sensu coś dodawać tylko pod ten potencjalny przypadek.
To dość rzadkie sytuacje ale się zdarzają, a dodanie interfejsu raczej nikomu nie przeszkadza - dodaje też czysty widok na interfejs, wiemy jakie ma metody, nie musimy scrollować całego pliku żeby zobaczyć co serwis oferuje.
Rozwiązaniem problemu "scrollowania dużego pliku" nie jest wydzielenie interfejsu, tylko podział klasy na mniejsze elementy.
Możemy też napisać sam interfejs i zlecić koledze z zespołu jego zaimplementowanie.
Zły pomysł. Wydłużasz feedback loop.