Ich versuche, GRASP zu lernen und ich fand dies erklärt ( hier auf Seite 3 ) über Low Coupling und ich war sehr überrascht, als ich dies fand:
Betrachten Sie die Methode
addTrack
für eineAlbum
Klasse, zwei mögliche Methoden sind:
addTrack( Track t )
und
addTrack( int no, String title, double duration )
Welche Methode reduziert die Kopplung? Das zweite ist der Fall, da die Klasse, die die Album-Klasse verwendet, keine Track-Klasse kennen muss. Im Allgemeinen sollten Parameter für Methoden Basistypen (int, char ...) und Klassen aus den Java. * -Paketen verwenden.
Ich neige dazu, damit einverstanden zu sein; Ich halte addTrack(Track t)
es addTrack(int no, String title, double duration)
aus verschiedenen Gründen für besser als :
Es ist immer besser, wenn eine Methode so wenig Parameter wie möglich enthält (laut Uncle Bobs Clean Code keiner oder einer, vorzugsweise 2 in einigen Fällen und 3 in besonderen Fällen; mehr als 3 müssen überarbeitet werden - dies sind natürlich Empfehlungen, keine holly rules). .
Wenn
addTrack
es sich um eine Methode einer Schnittstelle handelt und die Anforderungen erfordern, dass aTrack
mehr Informationen (z. B. Jahr oder Genre) enthält, muss die Schnittstelle geändert werden, damit die Methode einen anderen Parameter unterstützt.Die Kapselung ist kaputt. wenn
addTrack
es sich in einer schnittstelle befindet, dann sollte es die interna der nicht kennenTrack
.Es ist tatsächlich mehr auf die zweite Art und Weise gekoppelt, mit vielen Parametern. Angenommen, der
no
Parameter muss von nach geändert werdenint
,long
da mehr alsMAX_INT
Spuren vorhanden sind (oder aus welchem Grund auch immer). dann müssen sowohl dieTrack
als auch die Methode geändert werden, während wenn die MethodeaddTrack(Track track)
nur dieTrack
geändert werden würde.
Alle 4 Argumente sind tatsächlich miteinander verbunden, und einige von ihnen sind Konsequenzen aus anderen.
Welcher Ansatz ist besser?
Antworten:
Nun, Ihre ersten drei Punkte beziehen sich eigentlich auf andere Prinzipien als die Kopplung. Sie müssen immer ein Gleichgewicht zwischen oft widersprüchlichen Designprinzipien finden.
Ihr vierte Punkt ist über Kopplung, und ich stimme mit Ihnen. Die Kupplung ist über den Fluss von Daten zwischen den Modulen. Der Typ des Containers, in dem Daten fließen, ist weitgehend unerheblich. Wenn Sie eine Duration als Double statt als Field of A übergeben,
Track
müssen Sie sie nicht weitergeben. Die Module müssen weiterhin dieselbe Datenmenge und dieselbe Kopplungsmenge gemeinsam nutzen.Er betrachtet auch nicht alle Kopplungen im System als ein Aggregat. Die Einführung einer
Track
Klasse fügt zwar eine weitere Abhängigkeit zwischen zwei einzelnen Modulen hinzu, kann jedoch die Kopplung des Systems erheblich verringern , was hier die wichtige Maßnahme ist.Betrachten Sie beispielsweise eine Schaltfläche "Zur Wiedergabeliste hinzufügen" und ein
Playlist
Objekt. Das Einführen einesTrack
Objekts kann die Kopplung erhöhen, wenn Sie nur diese beiden Objekte berücksichtigen. Sie haben jetzt drei voneinander abhängige Klassen anstelle von zwei. Dies ist jedoch nicht die Gesamtheit Ihres Systems. Sie müssen auch die Spur importieren, die Spur abspielen, die Spur anzeigen usw. Das Hinzufügen einer weiteren Klasse zu dieser Mischung ist vernachlässigbar.Überlegen Sie nun, ob die Wiedergabe von Titeln über das Netzwerk und nicht nur lokal unterstützt werden soll. Sie müssen nur ein
NetworkTrack
Objekt erstellen , das der gleichen Schnittstelle entspricht. Ohne dasTrack
Objekt müssten Sie überall Funktionen erstellen wie:Das verdoppelt effektiv Ihre Kopplung und erfordert sogar Module, die sich nicht um die netzwerkspezifischen Dinge kümmern, um sie trotzdem zu verfolgen, um sie weitergeben zu können.
Ihr Welligkeitstest ist gut, um das wahre Ausmaß der Kopplung zu bestimmen. Es geht uns darum, die Stellen zu begrenzen, an denen sich eine Änderung auswirkt.
quelle
Meine Empfehlung lautet:
Verwenden
Aber stellen Sie sicher, dass dies
ITrack
eine Schnittstelle und keine konkrete Klasse ist.Album kennt die Interna der
ITrack
Implementierer nicht. Es ist nur an den Vertrag gekoppelt, der von derITrack
.Ich denke, dies ist die Lösung, die die geringste Menge an Kopplung erzeugt.
quelle
Track
, ob man dumm oder schlau ist.Track
ist eine Konkretion.ITrack
Schnittstelle ist eine Abstraktion. Auf diese Weise können Sie in Zukunft verschiedene Arten von Tracks verwenden, sofern diese den Anforderungen entsprechenITrack
.Ich würde argumentieren, dass die zweite Beispielmethode höchstwahrscheinlich die Kopplung erhöht , da sie höchstwahrscheinlich ein Track-Objekt instanziiert und es im aktuellen Album-Objekt speichert. (Wie in meinem obigen Kommentar vorgeschlagen, würde ich davon ausgehen, dass eine Album-Klasse irgendwo das Konzept einer Track-Klasse enthält.)
Bei der ersten Beispielmethode wird davon ausgegangen, dass ein Track außerhalb der Album-Klasse instanziiert wird. Daher können wir zumindest davon ausgehen, dass die Instanziierung der Track-Klasse nicht mit der Album-Klasse gekoppelt ist.
Wenn die Best Practices vorschlugen, dass wir niemals eine Klasse haben, die auf eine zweite Klasse verweist, würde die gesamte objektorientierte Programmierung aus dem Fenster geworfen.
quelle
Die Kopplung ist nur einer von vielen Aspekten, die Sie in Ihrem Code erhalten möchten. Indem Sie die Kopplung reduzieren, verbessern Sie nicht unbedingt Ihr Programm. Im Allgemeinen ist dies eine bewährte Methode, aber warum sollte dies in diesem speziellen Fall nicht
Track
bekannt sein?Durch die Verwendung einer
Track
Klasse, an die übergebenAlbum
werden soll, verbessern Sie die Lesbarkeit des Codes, und vor allem wandeln Sie, wie bereits erwähnt, eine statische Liste von Parametern in ein dynamisches Objekt um. Das macht Ihre Oberfläche letztendlich viel dynamischer.Sie erwähnen, dass die Kapselung kaputt ist, aber nicht.
Album
Die Interna von müssen bekannt seinTrack
, und wenn Sie kein Objekt verwenden,Album
müssen Sie alle Informationen kennen , die an das Objekt weitergegeben werden, bevor es sie trotzdem verwenden kann . Der Aufrufer muss auch die Interna kennenTrack
, da er einTrack
Objekt erstellen muss, aber der Aufrufer muss diese Informationen trotzdem kennen, wenn sie direkt an die Methode übergeben werden. Mit anderen Worten, wenn der Vorteil der Verkapselung darin besteht, den Inhalt eines Objekts nicht zu kennen, kann er in diesem Fall möglicherweise nicht verwendet werden, daAlbum
dieTrack
Informationen des Objekts trotzdem verwendet werden müssen .Sie möchten nicht verwenden,
Track
wennTrack
interne Logik enthalten ist, auf die der Anrufer keinen Zugriff haben soll. Mit anderen Worten, wennAlbum
es sich um eine Klasse handelt, die ein Programmierer, der Ihre Bibliothek verwendet, verwenden soll, möchten Sie nicht, dass er sie verwendet,Track
wenn Sie sagen: Rufen Sie eine Methode auf, um sie in der Datenbank beizubehalten. Das wahre Problem dabei liegt in der Tatsache, dass die Schnittstelle mit dem Modell verwickelt ist.Um das Problem zu beheben, müssen Sie
Track
in seine Schnittstellenkomponenten und seine Logikkomponenten trennen und zwei separate Klassen erstellen. Für den AnruferTrack
wird dies zu einer leichten Klasse, die Informationen enthalten und geringfügige Optimierungen (berechnete Daten und / oder Standardwerte) bieten soll. Im InnerenAlbum
würden Sie eine Klasse namens verwendenTrackDAO
, um das schwere Heben durchzuführen, das mit dem Speichern der InformationenTrack
in der Datenbank verbunden ist.Dies ist natürlich nur ein Beispiel. Ich bin mir sicher, dass dies überhaupt nicht Ihr Fall ist, und zögern Sie nicht, ohne
Track
Schuldgefühle zu handeln. Denken Sie daran, Ihren Anrufer beim Erstellen von Klassen im Auge zu behalten und bei Bedarf Schnittstellen zu erstellen.quelle
Beide sind richtig
ist besser (wie du schon argumentiert hast) während
ist weniger gekoppelt, da der verwendete Code
addTrack
nicht wissen muss, dass es eineTrack
Klasse gibt. Der Track kann beispielsweise umbenannt werden, ohne dass der Anrufcode aktualisiert werden muss.Während Sie über besser lesbaren / wartbaren Code sprechen, geht es in dem Artikel um das Koppeln . Weniger gekoppelter Code ist nicht unbedingt einfacher zu implementieren und zu verstehen.
quelle
Geringe Kopplung bedeutet nicht, dass keine Kopplung vorliegt. Irgendetwas muss irgendwo über Objekte in der Codebasis Bescheid wissen. Je geringer die Abhängigkeit von "benutzerdefinierten" Objekten ist, desto mehr Gründe geben Sie an, warum sich der Code ändert. Was der Autor, den Sie zitieren, mit der zweiten Funktion fördert, ist weniger gekoppelt, aber auch weniger objektorientiert, was der gesamten Idee von GRASP als objektorientierter Entwurfsmethodik widerspricht . Der springende Punkt ist, wie das System als Sammlung von Objekten und deren Interaktionen gestaltet werden kann. Sie zu meiden ist, als würde man einem das Autofahren beibringen, indem man sagt, man sollte stattdessen Fahrrad fahren.
Stattdessen besteht der richtige Weg darin, die Abhängigkeit von konkreten Objekten zu verringern , was die Theorie der "losen Kopplung" ist. Je weniger konkrete Typen eine Methode kennen muss, desto besser. Nur durch diese Aussage ist die erste Option tatsächlich weniger gekoppelt, da die zweite Methode, die die einfacheren Typen verwendet, über all diese einfacheren Typen Bescheid wissen muss. Sicher, sie sind integriert, und der Code in der Methode muss sich möglicherweise darum kümmern, aber die Signatur der Methode und die Aufrufer der Methode tun dies definitiv nicht . Wenn Sie einen dieser Parameter für eine konzeptionelle Audiospur ändern, sind weitere Änderungen erforderlich, wenn sie getrennt von denen in einem Spurobjekt (dem Punkt von Objekten; Kapselung) enthalten sind.
Wenn Sie einen Schritt weiter gehen und erwarten würden, dass Track durch etwas ersetzt wird, das die gleiche Aufgabe besser erfüllt, wäre möglicherweise eine Schnittstelle, die die erforderliche Funktionalität definiert, in Ordnung, ein ITrack. Dies könnte unterschiedliche Implementierungen wie "AnalogTrack", "CdTrack" und "Mp3Track" ermöglichen, die zusätzliche Informationen speziell für diese Formate liefern und gleichzeitig die grundlegende Datenexposition von ITrack bereitstellen, die konzeptionell eine "Spur" darstellt. ein endliches Teilstück von Audio. Track könnte ebenfalls eine abstrakte Basisklasse sein, aber dafür müssen Sie immer die in Track enthaltene Implementierung verwenden. Wenn Sie es als BetterTrack neu implementieren, müssen Sie die erwarteten Parameter ändern.
So lautet die goldene Regel; Programme und ihre Code-Komponenten haben immer Gründe, sich zu ändern. Sie können kein Programm schreiben, für das Sie niemals bereits geschriebenen Code bearbeiten müssen, um etwas Neues hinzuzufügen oder sein Verhalten zu ändern. In jeder Methode (GRASP, SOLID, jedes andere Akronym oder Modewort, das Sie sich vorstellen können) besteht Ihr Ziel einfach darin, die Dinge zu identifizieren, die sich im Laufe der Zeit ändern müssen, und das System so zu gestalten, dass diese Änderungen so einfach wie möglich sind (Übersetzt; Berühren Sie so wenig Codezeilen wie möglich und betreffen Sie so wenig andere Bereiche des Systems, die über den Rahmen Ihrer beabsichtigten Änderung hinausgehen.) In diesem Fall ist es am wahrscheinlichsten, dass ein Track mehr Datenelemente erhält, die addTrack () interessieren oder nicht Dieser Track wird durch BetterTrack ersetzt.
quelle