Frage

Lose Kupplung ist für einige Entwickler den heiligen Gral der gut engineerierten Software. Es ist sicherlich eine gute Sache, wenn der Code angesichts von Änderungen, die wahrscheinlich in absehbarer Zeit auftreten, flexibler macht oder Code -Duplizierung vermeidet.

Auf der anderen Seite erhöhen die Bemühungen, Komponenten locker zu koppeln, die Indirektion in einem Programm, wodurch die Komplexität erhöht wird, es oft schwieriger zu verstehen und es oft weniger effizient zu machen.

Berücksichtigen Sie einen Fokus auf eine lose Kopplung ohne Anwendungsfälle für die lockere Kopplung (z. B. die Vermeidung von Code-Duplikation oder Planung von Änderungen, die wahrscheinlich in absehbarer Zukunft auftreten), als Anti-Muster zu sein? Kann lose Kupplung unter den Dach von Yagni fallen?

War es hilfreich?

Lösung

Etwas.

Manchmal ist eine lockere Kupplung, die ohne zu viel Aufwand einhergeht, in Ordnung, auch wenn Sie keine spezifischen Anforderungen haben, die dazu gefordert werden, dass ein Modul entkoppelt wird. Die niedrig hängenden Früchte sozusagen.

Andererseits wird eine Übergine für lächerliche Mengen an Veränderungen unnötig Komplexität und Mühe sein. Yagni schlägt es auf den Kopf.

Andere Tipps

Ist Programmierpraxis X gut oder schlecht? Klar ist die Antwort stets "es hängt davon ab, ob."

Wenn Sie sich Ihren Code ansehen und sich fragen, welche "Muster" Sie injizieren können, dann machen Sie es falsch.

Wenn Sie Ihre Software so bauen, dass nicht verwandte Objekte nicht miteinander herumspielen, machen Sie es richtig.

Wenn Sie Ihre Lösung "Engineering" haben, damit sie unendlich erweitert und geändert werden kann, machen Sie sie tatsächlich komplizierter.

Ich denke, am Ende des Tages haben Sie die einzelne Wahrheit: Ist es mehr oder weniger kompliziert, die Objekte zu entkoppeln? Wenn es weniger kompliziert ist, sie zu koppeln, ist das die richtige Lösung. Wenn es weniger kompliziert ist, sie zu entkoppeln, ist dies die richtige Lösung.

(Ich arbeite derzeit in einer ziemlich kleinen Codebasis, die sehr kompliziert einen einfachen Job macht, und ein Teil dessen, was sie so kompliziert macht Entwickler.)

Ich denke, was Sie hier erhalten, ist das Konzept von Zusammenhalt. Hat dieser Code einen guten Zweck? Kann ich diesen Zweck verinnerlichen und das "Gesamtbild" dessen verstehen, was los ist?

Dies könnte zu schwer zu befolgendem Code führen, nicht nur, weil es noch viele weitere Quelldateien gibt (vorausgesetzt, es handelt sich um separate Klassen), sondern weil keine einzige Klasse einen Zweck zu haben scheint.

Aus agiler Sicht könnte ich vorschlagen, dass eine solche lockere Kopplung ein Anti-Muster wäre. Ohne den Zusammenhalt oder sogar die Anwendungsfälle dafür können Sie keine vernünftigen Einheiten -Tests schreiben und können den Zweck des Code nicht überprüfen. Der Agile-Code kann nun zu einer lockeren Kopplung führen, beispielsweise bei der Verwendung testgesteuerter Entwicklung. Aber wenn die richtigen Tests in der richtigen Reihenfolge erstellt wurden, gibt es wahrscheinlich sowohl eine gute Zusammenhalt als auch eine lose Kupplung. Und Sie sprechen nur über die Fälle, in denen eindeutig kein Zusammenhalt vorhanden ist.

Aus agiler Sicht wollen Sie diese künstliche Indirektion nicht, da es sich um etwas anstrengt, das wahrscheinlich sowieso nicht benötigt wird. Es ist viel einfacher, sich zu überarbeiten, wenn der Bedarf real ist.

Insgesamt möchten Sie eine hohe Kopplung in Ihren Modulen und eine lockere Kopplung zwischen ihnen. Ohne die hohe Kopplung haben Sie wahrscheinlich keinen Zusammenhalt.

Für die meisten Fragen wie diese lautet die Antwort "es hängt davon ab". Insgesamt, wenn ich ein Design locker locker auf eine Weise zusammenstellen kann, die sinnvoll ist, ohne dass ein großer Aufwand dies tut, werde ich es tun. Das Vermeiden unnötiger Kopplung im Code ist meiner Meinung nach ein völlig lohnendes Designziel.

Sobald es zu einer Situation kommt, in der es so aussieht, als ob Komponenten logischerweise eng gekoppelt werden sollten, werde ich nach einem überzeugenden Argument suchen, bevor ich anfange, sie zu trennen.

Ich denke, das Prinzip, an dem ich mit den meisten dieser Arten von Praxis arbeite, ist eine Trägheit. Ich habe eine Vorstellung davon, wie ich möchte, dass mein Code funktioniert und ob ich es so machen kann, ohne das Leben schwieriger zu machen, dann werde ich es tun. Wenn dies die Entwicklung schwieriger macht, aber die Wartung und die zukünftige Arbeit einfacher, werde ich versuchen, eine Vermutung zu erraten, ob es sich um mehr Arbeit des Codes handelt und dies als Leitfaden verwenden wird. Andernfalls müsste es ein absichtlicher Designpunkt sein, mit dem man sich lohnt.

Die einfache Antwort ist lockere Kupplung ist gut, wenn es richtig gemacht wird.

Wenn das Prinzip der Ein-Funktions, einem Zweck befolgt wird, sollte es leicht genug sein, dem zu folgen, was vor sich geht. Außerdem folgt ein loser Paarcode selbstverständlich ohne Anstrengung.

Einfache Designregeln: 1. Erstellen Sie das Wissen über mehrere Elemente nicht zu einem einzigen Punkt (wie überall hingewiesen, es hängt nicht davon ab), es sei denn, Sie bauen eine Fassadenschnittstelle auf. 2. Eine Funktion - ein Zweck (dieser Zweck kann facettenreich sein, wie in einer Fassade) 3. Ein Modul - ein klarer Satz miteinander verbundener Funktionen - ein klarer Zweck 4. Wenn Sie es nicht einfach testen können, dann hat es nicht ein einfacher Zweck

All diese Kommentare zu später leichter zu refaktor sind eine Ladung von Cods. Sobald das Wissen in viele Orte integriert ist, insbesondere in verteilten Systemen, die Kosten für das Refactoring, die Rollout -Synchronisation und fast alle anderen Kosten, die es so weit überlegen, dass das System in den meisten Fällen aus diesem Grund abgelöst wird.

Das Traurige an der Softwareentwicklung heutzutage ist, dass 90% der Menschen neue Systeme entwickeln und keine Fähigkeit haben, alte Systeme zu verstehen.

Es spielt keine Rolle, wie eng eine Sache zu einem anderen ist, wenn sich diese andere Sache nie ändert. Ich fand es im Laufe der Jahre im Allgemeinen produktiver, sich darauf zu konzentrieren, weniger Gründe zu suchen, um sich zu ändern, nach Stabilität zu suchen, als sie zu vereinfachen, indem sie versuchen, die lockerste Form der Kopplung zu erreichen.Entkopplung Ich habe festgestellt, dass ich sehr nützlich ist, bis zu dem Punkt, an dem ich manchmal eine bescheidene Code -Duplikation für die Entkopplungspakete bevorzuge. Als grundlegendes Beispiel hatte ich die Wahl, meine Mathematikbibliothek zu verwenden, um eine Bildbibliothek zu implementieren. Ich habe einige grundlegende Mathematikfunktionen nicht dupliziert, die trivial waren zu kopieren.

Jetzt ist meine Bildbibliothek völlig unabhängig von der Mathematikbibliothek in einer Weise, in der ich mich nicht auf die Bildbibliothek auswirkt. Das ist in erster Linie Stabilität. Die Bildbibliothek ist jetzt stabiler, da sie drastisch weniger Gründe für die Änderung haben, da sie von jeder anderen Bibliothek entkoppelt ist, die sich ändern könnte (neben der C -Standardbibliothek, die sich hoffentlich nie ändern sollte). Als Bonus ist es auch einfach zu bereitstellen, wenn es sich nur um eine eigenständige Bibliothek handelt, in der nicht ein paar andere Bibliotheken einziehen müssen, um sie zu bauen und zu verwenden.

Stabilität ist für mich sehr hilfreich. Ich mag es, eine Sammlung von gut getestetem Code zu erstellen, der immer weniger Gründe hat, sich in Zukunft jemals zu ändern. Das ist kein Pfeifentraum; Ich habe C -Code, den ich seit den späten 80ern verwendet habe und das ich seitdem überhaupt nicht geändert hat. Es ist zugegebenermaßen mit niedrigem Niveau wie pixelorientierte und geometrische Code, während ein Großteil meiner höheren Ebene veraltet wurde, aber es ist etwas, das immer noch viel dazu beiträgt. Das bedeutet fast immer eine Bibliothek, die auf immer weniger Dinge angewiesen ist, wenn überhaupt nichts Äußeres. Die Zuverlässigkeit steigt und erhöht sich, wenn Ihre Software zunehmend von stabilen Grundlagen abhängt, die nur wenige oder keine Gründe für die Änderung finden. Weniger bewegliche Teile sind wirklich schön, auch wenn in der Praxis die beweglichen Teile viel größer sind als die stabilen Teile. Es hilft meiner geistigen Gesundheit immer noch sehr zu wissen, dass die Gesamtheit einer Codebasis nicht aus beweglichen Teilen besteht.

Lose Kupplung ist in gleicher Weise, aber ich finde oft, dass lose Kupplung so viel weniger stabil ist als keine Kupplung. Wenn Sie nicht in einem Team mit weit überlegenen Schnittstellendesignern und Kunden arbeiten, die ihre Meinung nicht ändern, als ich jemals gearbeitet habe, finden auch reine Schnittstellen häufig Gründe, sich in einer Weise zu ändern, die immer noch Kaskadierungsbrüche im gesamten Code verursacht. Diese Idee, dass Stabilität erreicht werden kann, indem Abhängigkeiten zum Abstract anstelle des Betons geleitet werden, ist nur dann nützlich, wenn das Schnittstellendesign beim ersten Mal einfacher als die Implementierung zu erreichen ist. Ich finde es oft umgekehrt, wo ein Entwickler möglicherweise eine sehr gute, wenn nicht wundervolle Implementierung geschaffen hat, da er die Designanforderungen erfüllte, nur um in Zukunft zu finden, dass sich die Designanforderungen vollständig ändern. Das Design ist viel schwieriger, um richtig zu werden als die Implementierung, wenn Sie mich mit großen Codebasen fragen, die versuchen, sich ständig ändernde Anforderungen zu erfüllen. anfällig, geändert zu werden.

Daher bevorzuge ich Stabilität und vollständige Entkopplung, damit ich zumindest sicher sagen kann: "Diese kleine isolierte Bibliothek, die seit Jahren verwendet und durch gründliche Tests gesichert ist . " Es gibt mir ein kleines Stück geistiger Gesundheit, egal welche Art von Designänderungen nach außen gefordert werden.

Kopplung und Stabilität, ECS -Beispiel

Ich liebe auch Entitätskomponentensysteme und sie führen eine Menge enger Kopplung ein, da das System für Komponentenabhängigkeiten alle Zugriffe und die direkten Rohdaten direkt manipuliert, wie zum Beispiel:

enter image description here

Alle Abhängigkeiten hier sind ziemlich eng, da Komponenten nur Rohdaten freilegen. Die Abhängigkeiten fließen nicht zu Abstraktionen, sie fließen zu Rohdaten Dies bedeutet, dass jedes System über die maximale Art von Kenntnis verfügt, die jeder Art von Komponenten, die er zugreift, zugänglich ist. Komponenten haben keine Funktionalität mit allen Systemen, die mit den Rohdaten zugreifen und zu manipulieren. Es ist jedoch sehr einfach, ein System wie dieses zu begründen, da es so flach ist. Wenn eine Textur verrückt erscheint, wissen Sie sofort, dass nur das Rendering- und Malsystem zu Zugang zu Texturkomponenten zugreifen, und Sie können das Rendering -System wahrscheinlich schnell ausschließen, da sie nur konzeptionell aus Texturen liest.

Inzwischen könnte eine locker gekoppelte Alternative Folgendes sein:

enter image description here

... mit all den Abhängigkeiten, die zu abstrakten Funktionen fließen, nicht Daten und alles in diesem Diagramm, das eine eigene öffentliche Schnittstelle und Funktionalität enthüllt. Hier könnten alle Abhängigkeiten sehr locker sein. Die Objekte hängen möglicherweise nicht einmal direkt voneinander ab und interagieren durch reine Schnittstellen miteinander. Trotzdem ist es sehr schwer, dieses System zu begründen, besonders wenn etwas schief geht, angesichts des komplexen Gewandes der Interaktionen. Es wird auch mehr Interaktionen (mehr Kopplung, wenn auch lockerer) als die ECs geben, da die Entitäten über die Komponenten wissen müssen, die sie aggregieren, auch wenn sie nur die abstrakten öffentlichen Schnittstelle des anderen kennen.

Auch wenn es Designänderungen an irgendetwas gibt, erhalten Sie mehr Kaskadierungsbrüche als das ECS, und es gibt normalerweise mehr Gründe und Versuchungen für Designänderungen, da jedes einzelne Ding versucht, eine schöne objektorientierte Schnittstelle und Abstraktion zu liefern. Das kommt sofort mit der Idee einher, dass jedes einzelne kleine Ding dem Design Einschränkungen und Einschränkungen auferlegen wird, und diese Einschränkungen sind häufig das, was das Design der Entwurfswidrigkeit ändert. Die Funktionalität ist viel eingeschränkter und muss so viel mehr Designannahmen als Rohdaten treffen.

Ich habe festgestellt Damit die ECS -Version jemals vorhandene Komponenten ändern muss, da die Komponenten abhängig sind, haben keine Verantwortung, außer die entsprechenden Daten bereitzustellen, die für die Funktionen der Systeme benötigt werden. Vergleichen Sie die Schwierigkeit, ein Reines zu entwerfen IMotion Schnittstelle und konkretes Bewegungsobjekt implementieren diese Schnittstelle, die ausgefeilte Funktionen bietet und gleichzeitig versucht, Invarianten über private Daten im Vergleich zu einer Bewegungskomponente zu erhalten, die nur Rohdaten bereitstellen muss, die relevant sind, um das Problem zu lösen, und sich nicht mit der Funktionalität befasst.

Die Funktionalität ist so viel schwieriger, richtig zu werden als Daten, weshalb ich denke, dass es oft vorzuziehen ist, den Abhängigkeitsfluss in Richtung Daten zu richten. Wie viele Vektor-/Matrixbibliotheken gibt es doch draußen? Wie viele von ihnen verwenden genau die gleiche Datenrepräsentation und unterscheiden sich nur subtil in der Funktionalität? Unzählige und dennoch haben wir trotz identischer Datendarstellungen so viele, weil wir subtile Unterschiede in der Funktionalität wünschen. Wie viele Bildbibliotheken gibt es da draußen? Wie viele von ihnen repräsentieren Pixel auf eine andere und einzigartige Weise? Kaum und erneut zeigen, dass die Funktionalität viel instabiler und anfällig für Designänderungen ist als Daten in vielen Szenarien. Natürlich benötigen wir irgendwann Funktionalität, aber Sie können Systeme entwerfen, bei denen der Großteil der Abhängigkeiten in Richtung Daten fließt und nicht in Richtung Abstraktionen oder Funktionen im Allgemeinen. Dies würde die Stabilität über der Kopplung priorisieren.

Die stabilsten Funktionen, die ich je geschrieben habe (die Art, die ich seit den späten 80ern verwendete und wiederverwende, ohne sie ändern zu müssen) waren alle, die sich auf Rohdaten stützten, wie eine Geometriefunktion, die gerade ein Array von Annahme annahm Schwimmer und Ganzzahlen, nicht solche, die von einem Komplex abhängen Mesh Objekt oder IMesh Schnittstelle oder Vektor/Matrix -Multiplikation, von der gerade abhing float[] oder double[], nicht einer, von dem abhängig war FancyMatrixObjectWhichWillRequireDesignChangesNextYearAndDeprecateWhatWeUse.

Lizenziert unter: CC-BY-SA mit Zuschreibung
scroll top