Interfejs Segregacja Zasada mówi:
Żaden klient nie powinien być zmuszany do polegania na metodach, których nie używa. ISP dzieli bardzo duże interfejsy na mniejsze i bardziej szczegółowe, tak aby klienci musieli wiedzieć tylko o interesujących ich metodach.
Tutaj jest kilka pytań bez odpowiedzi. Jeden jest:
Jaki mały?
Mówisz:
Obecnie radzę sobie z tym, dzieląc przestrzeń nazw modułu w zależności od wymagań jego klientów.
Nazywam to ręcznym pisaniem kaczek . Budujesz interfejsy, które ujawniają tylko to, czego potrzebuje klient. Zasadą segregacji interfejsów nie jest po prostu ręczne pisanie kaczek.
Ale dostawca usług internetowych to nie tylko wezwanie do stworzenia „spójnych” interfejsów ról, które można ponownie wykorzystać. Żaden „spójny” interfejs interfejsu roli nie może doskonale zabezpieczyć się przed dodaniem nowego klienta z własnymi potrzebami.
ISP to sposób na odizolowanie klientów od wpływu zmian w usłudze. Jego celem było przyspieszenie kompilacji podczas wprowadzania zmian. Pewnie, że ma inne zalety, takie jak nie łamanie klientów, ale to był główny punkt. Jeśli zmieniam count()
podpis funkcji usług , dobrze jest, jeśli nieużywani klienci count()
nie muszą być edytowani i rekompilowani.
DLACZEGO zależy mi na zasadzie segregacji interfejsu. Nie uważam tego za tak ważne. Rozwiązuje prawdziwy problem.
Sposób, w jaki powinien być zastosowany, powinien rozwiązać problem. Nie ma rutynowego sposobu na zastosowanie ISP, którego nie można pokonać tylko odpowiednim przykładem potrzebnej zmiany. Powinieneś przyjrzeć się zmianom systemu i dokonać wyborów, które pozwolą ci się uspokoić. Sprawdźmy opcje.
Najpierw zadaj sobie pytanie: czy utrudnianie obecnie zmian w interfejsie usługi? Jeśli nie, wyjdź na zewnątrz i baw się, aż się uspokoisz. To nie jest ćwiczenie intelektualne. Upewnij się, że lekarstwo nie jest gorsze niż choroba.
Jeśli wielu klientów korzysta z tego samego podzbioru funkcji, przemawia to za „spójnymi” interfejsami wielokrotnego użytku. Podzbiór prawdopodobnie skupia się na jednym pomyśle, który możemy uznać za rolę, jaką usługa zapewnia klientowi. Miło, gdy to działa. To nie zawsze działa.
Jeśli wielu klientów korzysta z różnych podzbiorów funkcji, możliwe, że klient faktycznie korzysta z usługi przez wiele ról. Zgadza się, ale utrudnia dostrzeżenie ról. Znajdź ich i spróbuj się z nimi drażnić. To może nas z powrotem przenieść na przypadek 1. Klient po prostu korzysta z usługi za pośrednictwem więcej niż jednego interfejsu. Nie zaczynaj przesyłania usługi. Jeśli cokolwiek oznaczałoby to przekazanie usługi klientowi więcej niż jeden raz. To działa, ale zastanawiam się, czy usługa nie jest wielką kulą błota, którą należy rozbić.
Jeśli wielu klientów korzysta z różnych podzbiorów, ale nie widzisz ról, nawet pozwalając klientom korzystać z więcej niż jednego, to nie masz nic lepszego niż pisanie w klawiaturze do projektowania interfejsów. Ten sposób projektowania interfejsów zapewnia, że klient nie jest narażony na działanie nawet jednej funkcji, z której nie korzysta, ale prawie gwarantuje, że dodanie nowego klienta zawsze będzie wymagało dodania nowego interfejsu, którego wdrożenie usługi nie musi wiedzieć o tym będzie interfejs, który agreguje interfejsy ról. Po prostu wymieniliśmy jeden ból na inny.
Jeśli wielu klientów korzysta z różnych podzbiorów, nakładają się, oczekuje się, że zostaną dodani nowi klienci, którzy będą potrzebować nieprzewidzianych podzbiorów, a Ty nie chcesz przerywać usługi, a następnie rozważ bardziej funkcjonalne rozwiązanie. Ponieważ dwie pierwsze opcje nie zadziałały i naprawdę jesteś w złym miejscu, w którym nic nie jest zgodne z wzorcem i nadchodzą kolejne zmiany, zastanów się nad udostępnieniem każdej funkcji własnego interfejsu. Skończenie tutaj nie oznacza, że ISP zawiódł. Jeśli coś zawiodło, był to obiektowy paradygmat. Interfejsy z pojedynczą metodą w skrajnym przypadku podążają za ISP. To trochę pisanie na klawiaturze, ale może się nagle okazać, że interfejsy mogą być ponownie używane. Ponownie upewnij się, że nie ma
Okazuje się, że mogą stać się bardzo małe.
Podjąłem to pytanie jako wyzwanie do zastosowania ISP w najbardziej ekstremalnych przypadkach. Pamiętaj jednak, że najlepiej unikać skrajności. W dobrze przemyślanym projekcie stosującym inne zasady SOLID te problemy zwykle nie występują ani nie mają znaczenia, prawie w takim samym stopniu.
Kolejne pytanie bez odpowiedzi:
Kto jest właścicielem tych interfejsów?
W kółko widzę interfejsy zaprojektowane zgodnie z czymś, co nazywam mentalnością „biblioteki”. Wszyscy jesteśmy winni kodowania małpa-patrz-małpa-do, w którym po prostu coś robisz, ponieważ tak właśnie to widziałeś. To samo jesteśmy winni interfejsom.
Kiedy patrzę na interfejs zaprojektowany dla klasy w bibliotece, pomyślałem: o, ci faceci są zawodowcami. To musi być właściwy sposób wykonania interfejsu. Nie rozumiałem, że granica biblioteki ma swoje potrzeby i problemy. Po pierwsze, biblioteka całkowicie nie zna projektu swoich klientów. Nie każda granica jest taka sama. A czasami nawet ta sama granica ma różne sposoby jej przekroczenia.
Oto dwa proste sposoby spojrzenia na projekt interfejsu:
Interfejs serwisowy. Niektórzy ludzie projektują każdy interfejs, aby pokazać wszystko, co usługa może zrobić. Możesz nawet znaleźć opcje refaktoryzacji w IDE, które napiszą dla ciebie interfejs, używając dowolnej klasy, którą go karmisz.
Interfejs należący do klienta. Wydaje się, że ISP twierdzi, że jest to słuszne, a własność usługi jest błędna. Powinieneś rozbić każdy interfejs z myślą o potrzebach klientów. Ponieważ klient jest właścicielem interfejsu, powinien go zdefiniować.
Więc kto ma rację?
Rozważ wtyczki:
Kto jest właścicielem interfejsów tutaj? Klienci? Usługi?
Okazuje się jedno i drugie.
Kolory tutaj są warstwami. Czerwona warstwa (po prawej) nie powinna nic wiedzieć o zielonej warstwie (po lewej). Zieloną warstwę można zmienić lub zastąpić bez dotykania czerwonej warstwy. W ten sposób każda zielona warstwa może zostać podłączona do czerwonej warstwy.
Lubię wiedzieć, co powinno wiedzieć o czym, a czego nie powinno wiedzieć. Dla mnie „co wie o czym?” To najważniejsze pytanie architektoniczne.
Wyjaśnijmy trochę słownictwa:
[Client] --> [Interface] <|-- [Service]
----- Flow ----- of ----- control ---->
Klient jest czymś, co wykorzystuje.
Usługa jest czymś, co jest używane.
Interactor
tak się składa, że jest to jedno i drugie.
ISP twierdzi, że zrywają interfejsy dla klientów. Dobrze, zastosujmy to tutaj:
Presenter
(usługa) nie powinna dyktować Output Port <I>
interfejsu. Interfejs powinien zostać zawężony do Interactor
potrzeb (tutaj działających jako klient). Oznacza to, że interfejs WIE o Interactor
i, aby podążać za ISP, musi się z nim zmienić. I w porządku.
Interactor
(tutaj działający jako usługa) nie powinien dyktować Input Port <I>
interfejsu. Interfejs powinien zostać zawężony do Controller
potrzeb (klienta). Oznacza to, że interfejs WIE o Controller
i, aby podążać za ISP, musi się z nim zmienić. I to nie jest w porządku.
Drugi nie jest w porządku, ponieważ czerwona warstwa nie powinna wiedzieć o zielonej warstwie. Czy zatem dostawca usług internetowych się myli? A więc, coś w tym stylu, tak. Żadna zasada nie jest absolutna. Jest to przypadek, w którym głupcy, którzy lubią interfejs, aby pokazać wszystko, co usługa może zrobić, okazują się słuszni.
Przynajmniej mają rację, jeśli Interactor
nie robi nic poza potrzebami tego przypadku użycia. Jeśli Interactor
robi to dla innych przypadków użycia, nie ma powodu Input Port <I>
, aby to wiedzieć. Nie jestem pewien, dlaczego Interactor
nie można skupić się tylko na jednym przypadku użycia, więc nie stanowi to problemu, ale coś się dzieje.
Ale input port <I>
interfejs po prostu nie może podporządkować się Controller
klientowi i sprawić, że będzie to prawdziwa wtyczka. To granica „biblioteki”. Zupełnie inny sklep programistyczny mógłby pisać zieloną warstwę wiele lat po opublikowaniu czerwonej warstwy.
Jeśli przekraczasz granicę „biblioteki” i odczuwasz potrzebę zastosowania ISP, nawet jeśli nie jesteś właścicielem interfejsu po drugiej stronie, będziesz musiał znaleźć sposób na zawężenie interfejsu bez jego zmiany.
Jednym ze sposobów na wyciągnięcie tego jest adapter. Umieść go między klientami podobnymi Controler
a Input Port <I>
interfejsem. Adapter akceptuje Interactor
jako Input Port <I>
i przekazuje mu swoją pracę. Odsłania jednak tylko to, czego Controller
potrzebują klienci za pośrednictwem interfejsu roli lub interfejsów należących do zielonej warstwy. Adapter sam nie podąża za ISP, ale umożliwia bardziej złożoną klasę, taką jak Controller
korzystanie z ISP. Jest to przydatne, jeśli jest mniej adapterów niż klienci Controller
używający ich, a gdy znajdujesz się w nietypowej sytuacji, gdy przekraczasz granicę biblioteki i pomimo opublikowania biblioteka nie przestaje się zmieniać. Patrzę na ciebie Firefox. Teraz te zmiany tylko psują twoje adaptery.
Co to znaczy? Oznacza to, że szczerze mówiąc, nie dostarczyłeś mi wystarczających informacji, aby powiedzieć ci, co powinieneś zrobić. Nie wiem, czy niestosowanie się do usługodawcy internetowego powoduje problem. Nie wiem, czy przestrzeganie go nie spowoduje więcej problemów.
Wiem, że szukasz prostej zasady przewodniej. ISP stara się być tym. Ale pozostawia wiele niedopowiedzeń. Wierzę w to. Tak, nie zmuszaj klientów do polegania na metodach, których nie używają, bez ważnego powodu!
Jeśli masz dobry powód, np. Projektujesz coś, co akceptuje wtyczki, pamiętaj o problemach, które nie podążają za przyczynami dostawcy usług internetowych (trudno je zmienić bez łamania klientów) i sposobach na ich złagodzenie (utrzymaj Interactor
lub przynajmniej Input Port <I>
skoncentruj się na jednym stabilnym przypadek użycia).