Da das Prinzip der Schnittstellentrennung vorschlägt, sollte kein Client gezwungen werden, sich auf Methoden zu verlassen, die er nicht verwendet. Daher sollte ein Client keine leere Methode für seine Schnittstellenmethoden implementieren, andernfalls sollte diese Schnittstellenmethode in eine andere Schnittstelle eingefügt werden.
Aber wie wäre es mit konkreten Methoden? Sollte ich die Methoden trennen, die nicht jeder Client verwenden würde? Betrachten Sie die folgende Klasse:
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;
}
}
Im obigen Code verwendet CarShop die Methode isQualityPass () in Car überhaupt nicht, sollte ich isQualityPass () in eine neue Klasse aufteilen:
public class CheckCarQualityPass{
public boolean isQualityPass(Car car){
}
}
um die Kopplung von CarShop zu reduzieren? Weil ich einmal überlege, ob isQualityPass () zusätzliche Abhängigkeit benötigt, z.
public boolean isQualityPass(){
HttpClient client=...
}
CarShop würde von HttpClient abhängen, auch wenn es HttpClient eigentlich nie verwendet. Meine Frage lautet also: Sollte ich nach dem Prinzip der Schnittstellentrennung konkrete Methoden trennen, die nicht jeder Client verwenden würde, damit diese Methoden nur dann vom Client abhängen, wenn der Client sie tatsächlich verwendet, um die Kopplung zu verringern?
quelle
Car
Klasse haben, über die nicht alle Benutzer Bescheid wissen sollen, erstellen Sie (mehr als eine) Schnittstelle , die von derCar
Klasse implementiert wird und die nur Methoden deklariert, die im Schnittstellenkontext nützlich sind.Antworten:
In Ihrem Beispiel
CarShop
hängt dies nicht davon abisQualityPass
und es ist nicht gezwungen, eine leere Implementierung für eine Methode vorzunehmen. Es ist nicht einmal eine Schnittstelle beteiligt. Der Begriff "ISP" passt hier also einfach nicht zusammen. Und solange eine Methode wie dieseisQualityPass
eine Methode ist, die gut in dasCar
Objekt passt , ohne es mit zusätzlichen Verantwortlichkeiten oder Abhängigkeiten zu überlasten, ist dies in Ordnung. Es ist nicht erforderlich, eine öffentliche Methode einer Klasse an einen anderen Ort umzugestalten, nur weil ein Client vorhanden ist, der die Methode nicht verwendet.Es ist jedoch wahrscheinlich auch keine gute Idee , eine Domain-Klasse wie
Car
direkt von so etwas abhängig zu machenHttpClient
, unabhängig davon, welche Clients die Methode verwenden oder nicht. Das Verschieben der Logik in eine separate KlasseCheckCarQualityPass
wird einfach nicht als "ISP" bezeichnet, sondern als "Trennung von Bedenken" . Das Anliegen eines wiederverwendbaren Autoobjekts sollte wahrscheinlich nicht darin bestehen, externe HTTP-Aufrufe zu tätigen, zumindest nicht direkt. Dies schränkt die Wiederverwendbarkeit und darüber hinaus die Testbarkeit zu stark ein.Wenn
isQualityPass
dies nicht einfach in eine andere Klasse verschoben werden kann, besteht die Alternative darin, dieHttp
Aufrufe über eine abstrakte Schnittstelle zu tätigen,IHttpClient
in dieCar
zur Konstruktionszeit eingefügt wird, oder indem die gesamte Überprüfungsstrategie "QualityPass" (mit eingekapselter HTTP-Anforderung) in dasCar
Objekt eingefügt wird . Dies ist meiner Meinung nach jedoch nur die zweitbeste Lösung, da sie die Gesamtkomplexität erhöht, anstatt sie zu reduzieren.quelle
Car
Objekt führen. Es wäre nicht meine erste Wahl für eine Lösung (zumindest nicht im Zusammenhang mit diesem erfundenen Beispiel). Allerdings kann es im "echten" Code sinnvoller sein, ich weiß es nicht.Beim Prinzip der Schnittstellentrennung geht es nicht darum, den Zugriff auf das zu verbieten, was Sie nicht benötigen. Es geht darum, nicht darauf zu bestehen, auf das zuzugreifen, was Sie nicht benötigen.
Schnittstellen gehören nicht der Klasse, die sie implementiert. Sie gehören den Objekten, die sie verwenden.
Was hier verwendet wird, ist
getTax()
undgetCost()
. Worauf bestanden wird, ist alles zugänglich durchCar
. Das Problem besteht daraufCar
, dass der Zugriff darauf besteht, aufisQualityPass()
den nicht zugegriffen werden muss.Dies kann behoben werden. Sie fragen, ob es konkret behoben werden kann. Es kann.
Keiner dieser Codes weiß, ob
CarLiability
es sich um eine Schnittstelle oder eine konkrete Klasse handelt. Das ist gut. Es will es nicht wissen.Wenn es sich um eine Schnittstelle handelt, wird diese
Car
möglicherweise implementiert. Dies würde ISP nicht verletzen , denn obwohlisQuality()
in istCar
CarShop
nicht darauf bestehen. Das ist in Ordnung.Wenn es konkret ist, kann es sein, dass es
isQuality()
entweder nicht existiert oder an einen anderen Ort verlegt wurde. Das ist in Ordnung.Es kann auch sein, dass
CarLiability
es sich um eine konkrete HülleCar
handelt, die die Arbeit an sie delegiert. SolangeCarLiability
nicht aussetzt ,isQuality()
dannCarShop
ist in Ordnung. Natürlich wirft dies nur die Dose die Straße runter undCarLiability
muss herausfinden, wie man dem ISP aufCar
die gleiche Weise folgt, wieCarShop
es zu tun war.Kurz gesagt,
isQuality()
muss nichtCar
wegen ISP entfernt werden. Das impliziteisQuality()
Bedürfnis nach muss entfernt werden,CarShop
weilCarShop
es nicht benötigt wird, also sollte es nicht danach fragen.quelle
Nicht wirklich. Es gibt verschiedene Möglichkeiten , sich zu verstecken
Car.isQualityPass
ausCarShop
.1. Zugriff auf Modifikatoren
Aus Sicht des Demeter-Gesetzes könnten wir in Betracht ziehen
Car
undCardShop
keine Freunde sein . Es legitimiert uns, das nächste zu tun.Beachten Sie, dass sich beide Komponenten in unterschiedlichen Paketen befinden. Hat jetzt
CarShop
keine Sichtbarkeit überCar
geschützte Verhaltensweisen. (Entschuldigen Sie mich im Voraus, wenn das obige Beispiel so simpel aussieht).2. Schnittstellentrennung
Der ISP geht davon aus, dass wir mit Abstraktionen arbeiten, nicht mit konkreten Klassen. Ich gehe davon aus, dass Sie bereits vertraut sind mit der ISP Implementierung und die Rolle Schnittstellen .
Trotz der tatsächlichen
Car
Implementierung hindert uns nichts daran, ISP zu praktizieren.Was ich hier gemacht habe. Ich habe die Interaktion zwischen
Car
undCarShop
über die Rollenschnittstelle Billable eingegrenzt . Beachten Sie die Änderung an dergetPrice
Unterschrift. Ich habe das Argument absichtlich geändert. Ich wollte offensichtlich machen , dieCarShop
nur „gebunden / gebunden“ an einen der Rolle Schnittstellen zur Verfügung. Ich hätte die tatsächliche Implementierung verfolgen können, aber ich kenne die tatsächlichen Implementierungsdetails nicht und befürchte, dass die tatsächliche ImplementierunggetPrice(String carId)
Zugriff (Sichtbarkeit) auf die konkrete Klasse hat. Wenn dies der Fall ist, wird die gesamte Arbeit mit dem ISP nutzlos, da es in den Händen des Entwicklers liegt, Casting durchzuführen und nur mit der Schnittstelle Billable zu arbeiten . Egal wie methodisch wir sind, die Versuchung wird immer da sein.3. Einzelverantwortung
Ich fürchte, ich bin nicht in der Lage zu sagen, ob die Abhängigkeit zwischen
Car
undHttpClient
angemessen ist, aber ich stimme @DocBrown zu, es gibt einige Warnungen, die eine Entwurfsprüfung wert sind. Weder Demeters Gesetz noch ISP werden Ihr Design zu diesem Zeitpunkt "besser" machen. Sie werden das Problem nur maskieren, nicht beheben.Ich habe DocBrown das Strategiemuster als mögliche Lösung vorgeschlagen. Ich stimmte ihm zu, dass das Muster die Komplexität erhöht, aber ich denke auch, dass jede Neugestaltung dies tun wird. Es ist ein Kompromiss, je mehr Entkopplung wir wollen, desto mehr bewegliche Teile haben wir (normalerweise). Wie auch immer, ich denke beide sind sich einig, dass eine Neugestaltung sehr ratsam ist.
Zusammenfassen
Nein, Sie müssen keine konkreten Methoden in äußere Klassen verschieben, um sie nicht zugänglich zu machen. Es könnte unzählige Verbraucher geben. Würden Sie jedes Mal, wenn ein neuer Verbraucher ins Spiel kommt , alle konkreten Methoden in äußere Klassen verschieben ? Ich hoffe du nicht.
quelle