Ponieważ zasada segregacji interfejsów sugeruje, że żaden klient nie powinien być zmuszany do polegania na metodach, których nie używa, więc klient nie powinien implementować pustej metody dla swoich metod interfejsu, w przeciwnym razie ta metoda interfejsu powinna zostać umieszczona w innym interfejsie.
A co z konkretnymi metodami? Czy powinienem oddzielić metody, których nie używałby każdy klient? Rozważ następującą klasę:
public class Car{
....
public boolean isQualityPass(){
...
}
public int getTax(){
...
}
public int getCost(){
...
}
}
public class CarShop{
...
public int getCarPrice(int carId){
Car car=carList[carId];
int price=car.getTax() + car.getCost()... (some formula);
return price;
}
}
w powyższym kodzie CarShop w ogóle nie używa metody isQualityPass () w Car, czy powinienem oddzielić isQualityPass () do nowej klasy:
public class CheckCarQualityPass{
public boolean isQualityPass(Car car){
}
}
w celu zmniejszenia sprzężenia CarShop? Ponieważ raz myślę, że isQualityPass () potrzebuje dodatkowej zależności, np .:
public boolean isQualityPass(){
HttpClient client=...
}
CarShop zależałby od HttpClient, nawet tak naprawdę nigdy nie używa HttpClient. Więc moje pytanie brzmi: zgodnie z zasadą segregacji interfejsów, czy powinienem oddzielić konkretne metody, których nie używałby każdy klient, aby metody te zależały od klienta tylko wtedy, gdy klient faktycznie korzysta, aby zmniejszyć sprzężenie?
źródło
Car
klasie metodę , o której nie chcą (wszyscy) użytkownicy wiedzieć, to stwórz (więcej niż jeden) interfejs, któryCar
implementuje klasa, która deklaruje tylko metody przydatne w kontekście interfejsów.Odpowiedzi:
W twoim przykładzie
CarShop
nie zależyisQualityPass
i nie jest zmuszone do wykonania pustej implementacji dla metody. Nie ma nawet interfejsu. Zatem termin „ISP” po prostu nie pasuje tutaj. I dopóki taka metodaisQualityPass
jest metodą, która dobrze wpasowuje się wCar
obiekt, bez obciążania go dodatkowymi obowiązkami lub zależnościami, nie ma problemu. Nie ma potrzeby refaktoryzacji publicznej metody klasy w inne miejsce tylko dlatego, że istnieje jeden klient, który nie korzysta z tej metody.Jednak uzależnienie klasy domeny
Car
bezpośrednio od czegoś podobnegoHttpClient
prawdopodobnie nie jest dobrym pomysłem, niezależnie od tego, którzy klienci używają tej metody, czy nie. Przeniesienie logiki do oddzielnej klasyCheckCarQualityPass
po prostu nie nazywa się „ISP”, to się nazywa „rozdzielenie obaw” . Problemem samochodu wielokrotnego użytku nie powinno być prawdopodobnie wykonywanie jakichkolwiek zewnętrznych połączeń HTTP, przynajmniej nie bezpośrednio, co ogranicza możliwość ponownego użycia, a ponadto zbyt wiele możliwości testowania.Jeśli
isQualityPass
nie można go łatwo przenieść do innej klasy, alternatywą byłoby wykonanieHttp
wywołań za pomocą abstrakcyjnego interfejsu,IHttpClient
który jest wstrzykiwany wCar
czasie budowy, lub poprzez wstrzyknięcie całej strategii sprawdzania „QualityPass” (z enkapsulacją żądania HTTP) doCar
obiektu . Ale to jest IMHO tylko drugie najlepsze rozwiązanie, ponieważ zwiększa ogólną złożoność zamiast ją zmniejszać.źródło
Car
obiekt. To nie byłby mój pierwszy wybór rozwiązania (przynajmniej nie w kontekście tego wymyślonego przykładu). Może to jednak mieć więcej sensu w „prawdziwym” kodzie, nie wiem.Zasada segregacji interfejsu nie polega na uniemożliwieniu dostępu do tego, czego nie potrzebujesz. Chodzi o to, aby nie nalegać na dostęp do tego, czego nie potrzebujesz.
Interfejsy nie są własnością klasy, która je implementuje. Są własnością obiektów, które ich używają.
Zastosowano tutaj
getTax()
igetCost()
. Nalegane jest wszystko, przez co można uzyskać dostępCar
. Problem polega na naleganiuCar
oznacza, że nalega na dostęp, doisQualityPass()
którego nie jest potrzebny.Można to naprawić. Pytasz, czy można to naprawić konkretnie. To może.
Żaden z tych kodów nie wie nawet, czy
CarLiability
jest interfejsem czy konkretną klasą. To dobra rzecz. Nie chce wiedzieć.Jeśli jest to interfejs,
Car
może go zaimplementować. Nie naruszyłoby to usługodawcy internetowego, ponieważ nawet jeśliisQuality()
jest naCar
CarShop
to nie nalega. Jest okej.Jeśli jest konkretny, być może
isQuality()
nie istnieje lub został przeniesiony do innego miejsca. Jest okej.Być może
CarLiability
jest to konkretne opakowanie,Car
które deleguje mu pracę. Tak długo, jakCarLiability
nie narazićisQuality()
wtedyCarShop
jest w porządku. Oczywiście, to po prostu kopie puszkę w dół drogi iCarLiability
musi dowiedzieć się, jak podążać za ISP wCar
taki sam sposóbCarShop
.Krótko mówiąc,
isQuality()
nie trzeba go usuwać zCar
powodu usługodawcy internetowego. Implikowana potrzebaisQuality()
musi zostać usunięta,CarShop
ponieważCarShop
nie jest potrzebna, więc nie powinna o to prosić.źródło
Nie całkiem. Istnieją różne sposoby, aby ukryć
Car.isQualityPass
zCarShop
.1. Modyfikatory dostępu
Z punktu widzenia prawa Demetera moglibyśmy uważać się za przyjaciół
Car
iCardShop
nie być nimi . Usprawnia to nas do zrobienia następnego.Należy pamiętać, że oba składniki znajdują się w różnych pakietach. Teraz
CarShop
nie ma widoczności nadCar
chronionymi zachowaniami. (Przepraszam z góry, jeśli powyższy przykład wygląda na tak uproszczony).2. Segregacja interfejsu
ISP współpracuje z założenia, że możemy pracować z abstrakcji, a nie z konkretnych klas. Zakładam, że znasz już implementację ISP i interfejsy ról .
Mimo faktycznej
Car
implementacji nic nie powstrzymuje nas przed praktyką ISP.Co ja tu zrobiłem. Zawęziłem zakres interakcji między
Car
iCarShop
poprzez interfejs roli Billable . Pamiętaj o zmianiegetPrice
podpisu. Celowo zmodyfikowałem argument. Chciałem wyjaśnić, żeCarShop
jest on „związany / związany” tylko z jednym z dostępnych interfejsów ról . Mógłbym śledzić faktyczną implementację, ale nie znam prawdziwych szczegółów implementacji i obawiam się, że faktycznagetPrice(String carId)
ma dostęp (widoczność) do konkretnej klasy. Jeśli tak, cała praca wykonana z ISP staje się bezużyteczna, ponieważ to w rękach dewelopera jest casting i praca tylko z interfejsem Billable . Bez względu na to, jak jesteśmy metodyczni, pokusa będzie zawsze obecna.3. Jedna odpowiedzialność
Obawiam się, że nie jestem w stanie stwierdzić, czy zależność między
Car
iHttpClient
jest wystarczająca, ale zgadzam się z @DocBrown, budzi to pewne ostrzeżenia, które warte są przeglądu projektu. Ani prawo Demeter, ani dostawca usług internetowych nie uczynią twojego projektu „lepszym” w tym momencie. Po prostu zamaskują problem, a nie go naprawią.Jako możliwe rozwiązanie zasugerowałem DocBrown wzorzec strategii . Zgodziłem się z nim, że wzór dodaje złożoności, ale myślę również, że każde ponowne zaprojektowanie będzie. Jest to kompromis, im więcej chcemy oddzielić, tym więcej mamy ruchomych części (zwykle). W każdym razie uważam, że oboje zgadzają się z przeprojektowaniem jest wysoce wskazane.
Podsumowując
Nie, nie musisz przenosić konkretnych metod do klas zewnętrznych, ponieważ nie udostępniaj ich. Mogą istnieć niezliczeni konsumenci. Czy przenosiłbyś wszystkie konkretne metody do klas zewnętrznych za każdym razem, gdy pojawiał się nowy konsument ? Mam nadzieję, że nie.
źródło