Das Prinzip der Schnittstellentrennung lautet:
Kein Client sollte gezwungen werden, sich auf Methoden zu verlassen, die er nicht verwendet. ISP teilt sehr große Schnittstellen in kleinere und spezifischere auf, sodass die Kunden nur die Methoden kennen müssen, die für sie von Interesse sind.
Hier sind einige Fragen offen. Eins ist:
Wie klein?
Du sagst:
Gegenwärtig beschäftige ich mich damit, indem ich den Namespace des Moduls in Abhängigkeit von den Anforderungen seiner Kunden teile.
Ich nenne das manuelle Entenschreiben . Sie erstellen Schnittstellen, die nur die Anforderungen eines Clients offen legen. Das Prinzip der Schnittstellentrennung besteht nicht nur in der manuellen Eingabe von Enten.
Der ISP ist jedoch nicht nur ein Aufruf für "kohärente" Rollenschnittstellen, die wiederverwendet werden können. Kein "kohärentes" Design der Rollenschnittstelle kann perfekt verhindern, dass ein neuer Client mit eigenen Rollenanforderungen hinzugefügt wird.
ISP ist eine Möglichkeit, Clients von den Auswirkungen von Änderungen am Dienst zu isolieren. Damit sollte der Build schneller ausgeführt werden, wenn Sie Änderungen vornehmen. Sicher hat es andere Vorteile, wie nicht Kunden zu brechen, aber das war der Hauptpunkt. Wenn ich die count()
Signatur der Services- Funktion ändere, ist es hilfreich, wenn Clients, die sie nicht verwenden count()
, nicht bearbeitet und neu kompiliert werden müssen.
Dies ist der Grund, warum mir das Prinzip der Schnittstellentrennung am Herzen liegt. Es ist nicht etwas, was ich für wichtig halte. Es löst ein echtes Problem.
So sollte die Art und Weise, wie es angewendet werden sollte, ein Problem für Sie lösen. Es gibt keine hirntote Möglichkeit, ISP anzuwenden, die nicht mit dem richtigen Beispiel für eine notwendige Änderung besiegt werden kann. Sie sollten sich ansehen, wie sich das System ändert, und Entscheidungen treffen, die die Dinge beruhigen. Lassen Sie uns die Optionen untersuchen.
Stellen Sie sich zunächst die Frage, ob es derzeit schwierig ist, Änderungen an der Service-Oberfläche vorzunehmen. Wenn nicht, geh nach draußen und spiele, bis du dich beruhigt hast. Dies ist keine intellektuelle Übung. Bitte stellen Sie sicher, dass die Heilung nicht schlimmer ist als die Krankheit.
Wenn viele Clients dieselbe Teilmenge von Funktionen verwenden, spricht dies für "kohärente" wiederverwendbare Schnittstellen. Die Teilmenge konzentriert sich wahrscheinlich auf eine Idee, die wir uns als die Rolle vorstellen können, die der Service für den Kunden bereitstellt. Es ist schön, wenn das funktioniert. Das funktioniert nicht immer.
Wenn viele Clients unterschiedliche Teilmengen von Funktionen verwenden, verwendet der Client den Service möglicherweise tatsächlich über mehrere Rollen. Das ist in Ordnung, aber es macht die Rollen schwer zu sehen. Finde sie und versuche sie auseinander zu ziehen. Das könnte uns in Fall 1 zurückversetzen. Der Client nutzt den Service einfach über mehr als eine Schnittstelle. Bitte fange nicht an, den Dienst zu besetzen. Wenn überhaupt, würde dies bedeuten, dass der Service mehr als einmal an den Kunden weitergegeben wird. Das funktioniert, aber ich frage mich, ob der Service kein großer Schlammballen ist, der aufgebrochen werden muss.
Wenn viele Clients unterschiedliche Teilmengen verwenden, Sie jedoch keine Rollen sehen, bei denen die Clients möglicherweise mehr als eine Rolle verwenden, gibt es nichts Besseres als das Eingeben von Enten für die Gestaltung Ihrer Benutzeroberflächen. Auf diese Weise wird beim Entwerfen der Schnittstellen sichergestellt, dass der Client nicht einer einzigen Funktion ausgesetzt ist, die er nicht verwendet. Es wird jedoch fast garantiert, dass beim Hinzufügen eines neuen Clients immer eine neue Schnittstelle hinzugefügt wird, die die Serviceimplementierung nicht kennen muss darüber die Schnittstelle, die die Rollenschnittstellen aggregiert. Wir haben einfach einen Schmerz gegen einen anderen getauscht.
Wenn viele Clients unterschiedliche Teilmengen verwenden und sich überlappen, wird erwartet, dass neue Clients hinzugefügt werden, die unvorhersehbare Teilmengen benötigen, und Sie nicht bereit sind, den Dienst aufzulösen, sondern eine funktionalere Lösung in Betracht ziehen. Da die ersten beiden Optionen nicht funktionierten und Sie sich wirklich an einem schlechten Ort befinden, an dem nichts einem Muster folgt und weitere Änderungen bevorstehen, sollten Sie erwägen, jeder Funktion eine eigene Benutzeroberfläche bereitzustellen. Das bedeutet nicht, dass der ISP gescheitert ist. Wenn etwas fehlschlug, war es das objektorientierte Paradigma. Einzelmethodenschnittstellen folgen im Extremfall ISP. Es ist ein gutes Stück Tastatur-Eingabe, aber Sie werden feststellen, dass die Schnittstellen dadurch plötzlich wiederverwendbar werden. Stellen Sie sicher, dass es keine gibt.
Es stellt sich also heraus, dass sie tatsächlich sehr klein werden können.
Ich habe diese Frage als Herausforderung angesehen, um ISP in den extremsten Fällen anzuwenden. Bedenken Sie jedoch, dass Extreme am besten vermieden werden. In einem gut durchdachten Design, das andere SOLID-Prinzipien anwendet, treten diese Probleme normalerweise nicht auf oder spielen kaum eine Rolle.
Eine weitere unbeantwortete Frage lautet:
Wem gehören diese Schnittstellen?
Immer wieder sehe ich Schnittstellen, die mit einer "Bibliotheks" -Mentalität entworfen wurden. Wir haben uns alle der Affen-See-Affen-Do-Codierung schuldig gemacht, bei der Sie nur etwas tun, weil Sie es so gesehen haben. Wir sind an der gleichen Sache mit Schnittstellen schuld.
Wenn ich mir ein Interface anschaue, das für eine Klasse in einer Bibliothek entworfen wurde, habe ich gedacht: Oh, diese Leute sind Profis. Dies muss der richtige Weg sein, um eine Schnittstelle zu erstellen. Was ich nicht verstanden habe, ist, dass eine Bibliotheksgrenze ihre eigenen Bedürfnisse und Probleme hat. Zum einen kennt eine Bibliothek das Design ihrer Kunden überhaupt nicht. Nicht jede Grenze ist gleich. Und manchmal kann sogar dieselbe Grenze auf unterschiedliche Weise überschritten werden.
Es gibt zwei einfache Möglichkeiten, das Interface-Design zu betrachten:
Service-Schnittstelle. Einige Leute entwerfen jede Schnittstelle, um alles darzustellen, was ein Dienst tun kann. Sie können sogar Refactoring-Optionen in IDEs finden, die eine Schnittstelle für Sie schreiben, die die von Ihnen angegebene Klasse verwendet.
Client-Schnittstelle. Der ISP scheint zu argumentieren, dass dies richtig und der Service-Besitz falsch ist. Sie sollten jede Schnittstelle im Hinblick auf die Kundenanforderungen aufteilen. Da der Client die Schnittstelle besitzt, sollte er sie definieren.
Also, wer hat Recht?
Plugins berücksichtigen:
Wem gehören die Schnittstellen hier? Die Kunden? Die Dienste?
Es stellt sich heraus, beide.
Die Farben hier sind Schichten. Die rote Schicht (rechts) soll nichts über die grüne Schicht (links) wissen. Die grüne Schicht kann geändert oder ersetzt werden, ohne die rote Schicht zu berühren. Auf diese Weise kann eine beliebige grüne Schicht in die rote Schicht eingesteckt werden.
Ich mag es zu wissen, was darüber wissen soll und was nicht. Für mich ist "Was weiß wovon?" Die wichtigste architektonische Frage.
Lassen Sie uns einige Vokabeln klarstellen:
[Client] --> [Interface] <|-- [Service]
----- Flow ----- of ----- control ---->
Ein Client ist etwas, das verwendet.
Ein Service wird genutzt.
Interactor
zufällig beides.
Laut ISP werden Schnittstellen für Clients getrennt. Gut, lassen Sie uns das hier anwenden:
Presenter
(ein Dienst) sollte der Output Port <I>
Schnittstelle nicht diktieren . Die Schnittstelle sollte auf die Interactor
Anforderungen (hier als Client) beschränkt werden. Das bedeutet, dass sich die Schnittstelle, die über das Internet Interactor
und den Internetdienstanbieter Bescheid weiß, damit ändern muss. Und das ist in Ordnung.
Interactor
(hier als Dienst agierend) sollte der Input Port <I>
Schnittstelle nicht diktieren . Die Schnittstelle sollte auf die Controller
Bedürfnisse (eines Kunden) beschränkt werden. Das bedeutet, dass sich die Schnittstelle, die über das Internet Controller
und den Internetdienstanbieter Bescheid weiß, damit ändern muss. Und das ist nicht in Ordnung.
Die zweite ist nicht in Ordnung, weil die rote Schicht nichts über die grüne Schicht wissen soll. Ist ISP also falsch? Na irgendwie. Kein Prinzip ist absolut. Dies ist ein Fall, bei dem sich herausstellt, dass die Benutzeroberfläche, die alles zeigt, was der Service kann, in Ordnung ist.
Zumindest haben sie recht, wenn Interactor
sie nichts anderes tun, als dies für den Anwendungsfall erforderlich ist. Wenn die Interactor
Dinge für andere Anwendungsfälle erledigt werden, gibt es keinen Grund, warum dies Input Port <I>
bekannt sein sollte. Ich bin mir nicht sicher, warum ich mich Interactor
nicht nur auf einen Anwendungsfall konzentrieren kann. Dies ist kein Problem, aber es passiert etwas.
Aber das input port <I>
Interface kann sich einfach nicht dem Controller
Client unterordnen und muss ein echtes Plugin sein. Dies ist eine Bibliotheksgrenze. Ein völlig anderer Programmierladen könnte die grüne Schicht Jahre nach der Veröffentlichung der roten Schicht schreiben.
Wenn Sie eine Bibliotheksgrenze überschreiten und das Gefühl haben, ISP anwenden zu müssen, obwohl Ihnen die Schnittstelle auf der anderen Seite nicht gehört, müssen Sie einen Weg finden, die Schnittstelle einzugrenzen, ohne sie zu ändern.
Eine Möglichkeit, dies zu erreichen, ist ein Adapter. Setzen Sie es zwischen Clients wie Controler
und der Input Port <I>
Schnittstelle. Der Adapter übernimmt Interactor
als ein Input Port <I>
und die Arbeit delegiert. Controller
Über eine Rollenschnittstelle oder Schnittstellen, die der grünen Ebene gehören , werden jedoch nur die Anforderungen der Clients offengelegt. Der Adapter folgt nicht dem ISP selbst, sondern ermöglicht komplexeren Klassen Controller
, ISP zu genießen. Dies ist nützlich, wenn es weniger Adapter gibt, als solche Clients Controller
verwenden, und wenn Sie sich in einer ungewöhnlichen Situation befinden, in der Sie eine Bibliotheksgrenze überschreiten und die Bibliothek trotz Veröffentlichung nicht aufhört, sich zu ändern. Ich schaue dich an Firefox. Jetzt zerstören diese Änderungen nur Ihre Adapter.
Also, was bedeutet das? Es bedeutet ehrlich gesagt, dass Sie nicht genügend Informationen bereitgestellt haben, damit ich Ihnen sagen kann, was Sie tun sollten. Ich weiß nicht, ob Sie ein Problem haben, wenn Sie ISP nicht folgen. Ich weiß nicht, ob das Folgen nicht zu weiteren Problemen führen würde.
Ich weiß, dass Sie nach einem einfachen Leitsatz suchen. ISP versucht das zu sein. Aber es bleibt viel ungesagt. Ich glaube daran. Ja, bitte zwingen Sie Kunden nicht, sich ohne guten Grund auf Methoden zu verlassen, die sie nicht anwenden!
Wenn Sie einen guten Grund haben, z. B. das Entwerfen von Plugins, müssen Sie sich der Probleme bewusst sein, die nicht den Ursachen des Internetdienstanbieters entsprechen (es ist schwierig, Änderungen vorzunehmen, ohne die Clients zu beschädigen), und der Möglichkeiten, diese zu entschärfen (behalten Interactor
oder sich zumindest Input Port <I>
auf einen Stall zu konzentrieren) Anwendungsfall).