Wie viel Aufwand sollte ich in die Erstellung lose gekoppelter Designs investieren?

10

Ich lerne gerade über Designmuster.

Ich denke, die meisten Leute würden zustimmen, dass diese Muster großartige Werkzeuge sind, aber mit Mäßigung und nicht als Antwort auf alles verwendet werden sollten. Eine zu häufige Verwendung würde die Anwendung mit geringem Nutzen überkomplizieren. Muster sollten nur verwendet werden, wenn sie die beste Lösung sind oder bei der Erstellung einer guten Lösung helfen können (stimmen Sie zu?).

Mit dieser Einstellung:

Das Buch, das ich lese (Head First Design Patterns), betont häufig die Bedeutung der losen Kopplung . Diese lose Kopplung wird erreicht, indem Prinzipien wie "Programmieren an eine Schnittstelle, keine Implementierung" und "Kapselung, was variiert" befolgt werden.

Grundsätzlich existieren die meisten Muster, die ich bisher gelernt habe, hauptsächlich, um Designs lose zu koppeln und damit flexibler zu machen.

Ich verstehe die Bedeutung und die Vorteile einer losen Kopplung.

Meine Frage ist jedoch, wie viel Aufwand man tatsächlich in die Schaffung lose gekoppelter, flexibler Designs investieren sollte.

Diejenigen, die sich Designmustern widersetzen, sagen, dass die Kosten für die Verwendung dieser Muster häufig die Vorteile überwiegen. Sie investieren viel Zeit in die Erstellung eines lose gekoppelten Designs unter Verwendung eines Musters, wobei in der Realität - die lose Kopplung, das Programmieren auf eine Schnittstelle, keine Implementierung und all diese Prinzipien - möglicherweise nicht so wichtig sind.

Was ich wissen möchte, ist, wie viel Aufwand ich tatsächlich in die Erstellung zusätzlicher Abstraktionsebenen und Designs investieren sollte, damit meine Anwendung OO-Prinzipien wie lose Kopplung, Programmierung an eine Schnittstelle usw. folgen kann. Lohnt es sich wirklich? es? Wie viel Aufwand sollte ich in diese investieren?

Aviv Cohn
quelle
Einige Leute setzen das ganze Leben dafür ein, zum Beispiel Kent Beck, Leute, die es schätzen, würden Ihre Bemühungen sehen.
Niing

Antworten:

14

Fette Antwort: Je mehr Sie es tun, desto weniger Aufwand ist es.

Grundsätzlich ist es nicht sehr schwierig, lose gekoppelten Code zu erstellen. Das Trainieren Ihres Geistes, um in Bezug auf lose Kopplung zu denken, ist andererseits. Und ich glaube, es ist es absolut wert.

Angenommen, Sie nehmen einen Job an, der einen iterativen Ansatz für die Softwareentwicklung verwendet, wie er von den meisten in den letzten 20 Jahren empfohlen wurde. Sie erstellen etwas, das in Iteration 1 wunderbar funktioniert. In Iteration 2 bauen Sie darauf auf, fügen einige neue Funktionen hinzu und biegen ein oder zwei Dinge ein wenig, wenn sie nicht in Ihr Iterationskonzept 1 passen, wie die Dinge funktionieren sollen . Jetzt kommt Iteration 3, und Sie finden heraus, welche Anforderungen Sie benötigen, um Ihre grundlegende Architektur zu überdenken. Wie können Sie Ihren Code zerreißen und neu erstellen, ohne zum ersten Punkt zurückzukehren?

Das passiert. Viel. Entweder laufen Projekte zu spät, oder alle haben zu viel Angst, das zu tun, was in späteren Iterationen getan werden muss. Im besten Fall erhalten Sie einen großen Schlammball. Dinge wie lose Kopplung und das Prinzip der Einzelverantwortung mildern diese Probleme in hohem Maße. Deshalb werden SOLID-Prinzipien angepriesen - sie helfen wirklich.

Und Sie werden feststellen, dass es nach ein paar lose gekoppelten Designs ganz natürlich wird. Muster sind Dinge, die Menschen gefunden haben und die für sie gearbeitet haben, also haben sie sie dokumentiert. Sie sind nützliche Werkzeuge, die natürlich auch mit der Zeit kommen.

Matthew Flynn
quelle
5

Der erforderliche Aufwand wird Ihnen nicht immer genug sagen. Ich denke, eine bessere Maßnahme wäre das Verhältnis von Aufwand zu Auszahlung. Es ist jedoch nicht immer einfach und fehlerfrei, herauszufinden, wie hoch die Auszahlung sein könnte.

Bei Mustern, die die Flexibilität erhöhen, ist zu beachten, dass das Einfügen einige Muster kostet. Wenn Sie nicht genau wissen, warum Sie sie benötigen, kann es sein, dass Sie Komplikationen im Code haben, diese jedoch nie verwenden. Wenn die Flexibilität niemals gebogen wird, kann sie genauso gut nicht vorhanden sein.

Es gibt eine Sache, die Sie immer tun sollten (oder so nah wie möglich daran): die einzelnen Komponenten Ihrer Software (Objekte für OOP, Funktionen für funktionale oder prozedurale Programmierung) in Black Boxes zu verwandeln. Eine Black Box ist etwas, dessen Innenseiten (Implementierung) wir nicht kennen oder für das wir uns nicht interessieren.

Wenn wir nicht wissen oder uns nicht darum kümmern, wie es funktioniert, werden wir nicht versuchen, etwas anderes als die Schnittstelle zu verwenden. Wenn sich also die Implementierung ändert, ohne die Schnittstelle zu ändern, ist dies nur innerhalb der Black Box von Bedeutung - nichts außerhalb davon betroffen sein kann. Dies erleichtert auch das Nachdenken über das Programm, da Sie nicht jedes einzelne Detail des gesamten Programms im Kopf behalten müssen. Je mehr Details Sie zu Recht vergessen können, desto mehr Raum ist in Ihrem Kopf für die Details, die wichtig sind.

Ich denke, Sie haben die Einwände gegen das Entwerfen von Mustern falsch verstanden. Ich bin kein Fan von ihnen, aber ich mag das Prinzip der losen Kopplung und Programmierung an eine Schnittstelle sehr. Mein stärkster Einwand gegen sie ist, dass sie als vorgefertigte Lösung präsentiert werden, die nicht das Verständnis der dahinter stehenden Prinzipien fördert, sondern das Auswendiglernen von Details fördert. Ich werde nicht empfehlen, dass Sie sie nicht studieren, aber wenn Sie dies tun, denken Sie daran, dass das Verständnis der Prinzipien dahinter wichtiger ist als die Besonderheiten eines bestimmten Musters.

Eine der Einwände gegen das Entwerfen von Mustern ist, dass sie "offensichtlich" sind oder dass "ich sie verwendet habe, bevor ich von ihnen gehört habe, nur nicht unter diesem Namen". Der Grund, warum Menschen diese Einwände erheben können oder die Urheber von Entwurfsmustern sie überhaupt erst einfallen lassen könnten, ist, dass sie die Prinzipien dahinter verstanden haben.

Die Verwendung von Black Boxes organisiert Ihren Code auf vernünftige Weise und kostet normalerweise nicht viel (wenn überhaupt). benutze sie überall. Die Verwendung eines Entwurfsmusters oder einer anderen Technik, die eine Abstraktionsebene hinzufügt oder die Dinge kompliziert, ist mit Kosten verbunden. Verwenden Sie sie niemals, nur weil sie hübsch oder ordentlich oder klug sind. Verwenden Sie sie, wenn sie zum Problem passen und die Kosten es wert sind, bezahlt zu werden, um den Nutzen zu erzielen.

Michael Shaw
quelle
4

Ihre Frage scheint eine lose Kopplung mit Entwurfsmustern gleichzusetzen, aber sie sind wirklich getrennte, aber verwandte Dinge.

Die lose Kopplung ist ein grundlegendes Anliegen bei der Programmierung. Sobald wir uns vom Maschinencode wegbewegten und in symbolische Sprachen wechselten, wurden Programme lose mit ihrer Ausführung verbunden. Usw.

OO selbst ist im Grunde ein Modell zum Erstellen lose gekoppelter Komponenten. Durch die Kapselung werden die Interna von Objekten lose mit anderen Objekten gekoppelt. Funktionale Programmierung vielleicht sogar noch mehr, weil die Funktionsausführung im Extremfall lose mit der Ausführung anderer Funktionen gekoppelt ist.

Fast per Definition beinhaltet jedes Design, das Sie ausführen, eine lose Kopplung. Es gibt Möglichkeiten , wie Sie arrangieren Dinge gekoppelt haben eng versehentlich, aber ich habe nur ein paar Fälle gesehen , wo jemand tatsächlich ein System entworfen werden mehr eng gekoppelt.

(Hin und wieder arbeite ich mit jemandem zusammen, der verhindern möchte, dass zukünftige Entwickler Designentscheidungen treffen, indem er statische Verweise auf bestimmte Framework-Artefakte einbettet und deren Verwendung erzwingt - absichtlich. Aber das ist wirklich die Ausnahme. Das meiste Design dreht sich darum, Dinge für Modularität aufzubrechen und in vielen Zusammenhängen wiederverwenden.)

rauben
quelle
3

Muster sollten nur verwendet werden, wenn sie die beste Lösung sind oder bei der Erstellung einer guten Lösung helfen können (stimmen Sie zu?).

Ich sehe Designmuster ausschließlich als Implementierungsdetails. Wenn Sie Ihre öffentlichen APIs und Programme in dieser Dokumentation dokumentieren, spielt es im Allgemeinen keine Rolle (oder beeinflusst Sie stark), wo Sie Entwurfsmuster haben. Das heißt, Sie sagen nicht "Ich habe hier ein Brückenmuster und ich werde einen Besucher darüber implementieren". Stattdessen heißt es: "Diese Klasse wird auf verschiedenen Betriebssystemen unterschiedliche Implementierungen haben, sodass sie unter Verwendung eines Brückenmusters implementiert wird." Wenn Sie es dann verwenden, ist es Ihnen gleichgültig, ob es als Bridge implementiert wird - da Sie sich die öffentliche API und nicht das Bridge-Muster ansehen.

Wie viel Aufwand sollte man tatsächlich in die Schaffung lose gekoppelter, flexibler Designs investieren?

Eine lose Kopplung kann durch Befolgen eines einfachen Regelwerks erreicht werden. Wenn Sie diese respektieren, wird Ihr Code beim Schreiben (mehr) lose gekoppelt (dh jeder Aufwand ist bereits Teil des Entwicklungsprozesses).

Unter den Regeln (keine vollständige Liste):

  • Definieren Sie Ihre Schnittstellen, indem Sie Client-Code denken (oder schreiben) (wie die Klasse verwendet wird), nicht was die Klasse tun wird (dh die Schnittstelle entwerfen, nicht implementieren).
  • "sag, frag nicht"
  • Konstruieren Sie Objekte aus bereits erstellten Teilen
  • Übergeben Sie die tatsächlichen Objekte, die Sie verwenden werden, an den Konstruktor (keine Fabriken für die Mitglieder, Parameter für die Fabriken der Parameter oder ähnliches).
  • TROCKEN (Wenn Sie zwei Zeilen haben, die an zwei Stellen in derselben Reihenfolge angezeigt werden, extrahieren Sie sie in eine separate Funktion usw.).
  • Wenn die Erstellung eines Objekts eine komplexere Operation ist, implementieren Sie die Erstellung der Zwischenteile als Factory-Methode / -Klasse (dh nicht im Konstruktorkörper).
  • YAGNI (erstelle Dinge so, wie du sie brauchst, nicht vorher).

Diese Regeln werden je nach Sprache, Entwicklungsmethode Ihres Teams (z. B. TDD), Zeitbudgetbeschränkungen usw. unterschiedlich befolgt.

In Java empfiehlt es sich beispielsweise, Ihre Schnittstelle als zu definieren interfaceund Clientcode darauf zu schreiben (instanziieren Sie dann die Schnittstelle mit einer Implementierungsklasse).

In C ++ hingegen haben Sie keine Schnittstellen, sodass Sie die Schnittstelle nur als abstrakte Basisklasse schreiben können. Da Sie in C ++ die Vererbung nur verwenden, wenn Sie eine starke Anforderung dafür haben (und als solche den Overhead unnötiger virtueller Funktionen vermeiden), werden Sie die Schnittstelle wahrscheinlich nicht separat definieren, sondern nur den Klassenkopf.

Diejenigen, die sich Designmustern widersetzen, sagen, dass die Kosten für die Verwendung dieser Muster häufig die Vorteile überwiegen.

Ich denke, sie machen es falsch. Wenn Sie lose gekoppelten (und DRY-) Code schreiben, ist die Integration von Entwurfsmustern mit minimalem zusätzlichen Aufwand erforderlich. Andernfalls müssen Sie Ihren Code anpassen, um ein Entwurfsmuster zu implementieren.

Wenn Sie viele Änderungen vornehmen müssen, um ein Entwurfsmuster zu implementieren, liegt Ihr Problem nicht im Entwurfsmuster, sondern darin, dass Ihre Codebasis monolithisch und eng gekoppelt ist. Dies ist ein schlechtes / suboptimales Designproblem, kein Designmusterproblem.

Was ich wissen möchte, ist, wie viel Aufwand ich tatsächlich in die Erstellung zusätzlicher Abstraktionsebenen und Designs investieren sollte, damit meine Anwendung OO-Prinzipien wie lose Kopplung, Programmierung an eine Schnittstelle usw. folgen kann. Lohnt es sich wirklich? es? Wie viel Aufwand sollte ich in diese investieren?

Ihre Fragen gehen von der (nicht angegebenen) Annahme aus, dass der einzige Vorteil der losen Kopplung die Fähigkeit ist, Entwurfsmuster einfach zu implementieren. Es ist nicht.

Zu den Vorteilen der losen Kupplung gehören:

  • Flexibilität bei Umgestaltung und Neugestaltung
  • weniger verschwendete Mühe
  • Testbarkeit
  • erhöhte Möglichkeit, den Code wiederzuverwenden
  • Design Einfachheit
  • weniger Zeit im Debugger verbracht

... und ein paar andere, die mir momentan nicht in den Sinn kommen.

utnapistim
quelle