Ich habe dieses Wiki über das Stable Abstractions Principle (SAP) gelesen .
Die SAP gibt an, dass ein Paket umso abstrakter sein sollte, je stabiler es ist. Dies bedeutet, dass ein Paket, das weniger stabil ist (sich eher ändert), konkreter sein sollte. Was ich nicht wirklich verstehe, ist, warum dies der Fall sein sollte. Sicherlich sollten wir unabhängig von der Stabilität in allen Fällen auf Abstraktionen angewiesen sein und die konkrete Umsetzung verbergen?
design
design-patterns
architecture
object-oriented-design
design-principles
SteveCallender
quelle
quelle
Antworten:
Stellen Sie sich Ihre Pakete als API vor, um das Beispiel aus dem Papier zu übernehmen und Definitionen für
Reader
mitstring Reader.Read()
undWriter
mitvoid Writer.Write(string)
als Ihre abstrakte API zu verwenden.Sie können dann eine Klasse
Copy
mit einer MethodeCopier.Copy(Reader, Writer)
und der ImplementierungWriter.Write(Reader.Read())
sowie möglicherweise einigen Überprüfungen der Integrität erstellen .Nun machen Sie konkrete Implementierungen, zB
FileReader
,FileWriter
,KeyboardReader
undDownloadThingsFromTheInternetReader
.Was ist, wenn Sie Ihre Implementierung von ändern möchten
FileReader
? Kein Problem, ändern Sie einfach die Klasse und kompilieren Sie neu.Was ist, wenn Sie die Definition Ihrer Abstraktion ändern möchten
Reader
? Hoppla, Sie können nicht nur das ändern, aber Sie werden auch ändern müssenCopier
,FileReader
,KeyboardReader
undDownloadThingsFromTheInternetReader
.Dies ist der Grund für das Prinzip der stabilen Abstraktion: Machen Sie Ihre Konkretisierungen weniger stabil als die Abstraktionen.
quelle
Wegen YAGNI .
Wenn Sie derzeit nur eine Implementierung einer Sache haben , warum sollten Sie sich dann mit einer zusätzlichen und nutzlosen Ebene beschäftigen? Dies führt nur zu unnötiger Komplexität. Schlimmer noch, manchmal liefern Sie eine Abstraktion , die an den Tag denkt, an dem eine zweite Implementierung kommen wird ... und dieser Tag passiert nie. Was für eine Verschwendung von Arbeit!
Ich denke auch, dass die eigentliche Frage, die sich stellt, nicht lautet: "Muss ich mich auf Abstraktionen verlassen?" sondern "Brauche ich Modularität?". Und Modularität ist nicht immer erforderlich, siehe unten.
In der Firma, in der ich arbeite, sind einige der von mir entwickelten Softwareprodukte stark an ein Hardwaregerät gebunden, mit dem es kommunizieren muss. Diese Geräte wurden entwickelt, um ganz bestimmte Ziele zu erreichen und sind alles andere als modular. :-) Einmal geht das erste produzierte Gerät der Fabrik und ist irgendwo, die beide nie Änderung der Firmware installiert und Hardware kann, je .
Ich kann also sicher sein, dass sich einige Teile der Software niemals weiterentwickeln werden. Diese Teile müssen nicht von Abstraktionen abhängen, da es nur eine Implementierung gibt und diese sich niemals ändern wird. Das Deklarieren von Abstraktionen für diese Codeteile verwirrt nur alle und nimmt mehr Zeit in Anspruch (ohne Wert zu erzeugen).
quelle
Ich denke, Sie sind vielleicht verwirrt von dem Wort Stall , das Robert Martin gewählt hat. Hier beginnt meiner Meinung nach die Verwirrung:
Wenn Sie den Originalartikel durchlesen , werden Sie sehen (Hervorhebung von mir):
Ich habe immer mit der Wahl des Autors für das Wort " stabil" zu kämpfen gehabt , da ich (wie Sie) dazu neige, an den "Wahrscheinlichkeits" -Aspekt der Stabilität zu denken, dh wahrscheinlich nicht zu ändern . Die Schwierigkeit impliziert, dass das Ändern dieses Moduls viele andere Module kaputt macht, und es wird eine Menge Arbeit sein, den Code zu reparieren.
Martin verwendet auch die Wörter unabhängig und verantwortungsbewusst , die mir viel mehr Bedeutung vermitteln. In seinem Schulungsseminar verwendete er eine Metapher über Eltern von Kindern, die aufwachsen, und wie sie "verantwortlich" sein sollten, weil ihre Kinder von ihnen abhängig sind. Scheidung, Arbeitslosigkeit, Inhaftierung usw. sind Beispiele aus der Praxis für die negativen Auswirkungen, die Veränderungen bei den Eltern auf Kinder haben werden. Daher sollten Eltern zum Wohle ihrer Kinder "stabil" sein. Übrigens ist diese Metapher von Kindern / Eltern nicht unbedingt mit der Vererbung in OOP verbunden!
Dem Geist des "Verantwortlichen" folgend, fand ich alternative Bedeutungen für schwer zu ändern (oder sollte sich nicht ändern ):
Fügen Sie diese Definitionen in die Anweisung ein
Lassen Sie uns das Stable Abstractions Principle (SAP) zitieren und die verwirrenden Wörter stabil / instabil hervorheben:
Klarstellung ohne diese verwirrenden Worte:
TL; DR
Der Titel Ihrer Frage lautet:
Ich denke, wenn Sie die Abstraktionen richtig erstellen (z. B. existieren sie, weil viel Code von ihnen abhängt), gibt es keine wesentlichen Nachteile.
quelle
Abstraktionen sind Dinge, die in der Software schwer zu ändern sind, weil alles von ihnen abhängt. Wenn sich Ihr Paket häufig ändert und Abstraktionen enthält, müssen Personen, die davon abhängig sind, einen großen Teil ihres Codes neu schreiben, wenn Sie etwas ändern. Wenn Ihr instabiles Paket jedoch einige konkrete Implementierungen enthält, muss nach Änderungen viel weniger Code neu geschrieben werden.
Wenn sich Ihr Paket also häufig ändert, sollte es besser Betone und keine Abstraktionen bereitstellen. Sonst ... wer zum Teufel wird es benutzen? ;)
quelle
Denken Sie an Martins Stabilitätsmetrik und was er unter "Stabilität" versteht:
Oder:
Das heißt, ein Paket wird als völlig instabil betrachtet, wenn alle seine Abhängigkeiten ausgehen: Es verwendet andere Dinge, aber nichts verwendet es. In diesem Fall ist es nur sinnvoll, dass das Ding konkret ist. Es wird auch die am einfachsten zu ändernde Art von Code sein, da nichts anderes ihn verwendet und daher nichts anderes kaputt gehen kann, wenn dieser Code geändert wird.
Wenn Sie in der Zwischenzeit das gegenteilige Szenario einer vollständigen "Stabilität" mit einem Paket haben, das von einem oder mehreren Dingen verwendet wird, aber nichts für sich allein verwendet, wie ein zentrales Paket, das von der Software verwendet wird, dann sagt Martin, dass dies der Fall sein sollte abstrakt. Dies wird auch durch den DIP-Teil von SOLI (D), das Prinzip der Abhängigkeitsinversion, verstärkt, der im Wesentlichen besagt, dass Abhängigkeiten für Code auf niedriger und hoher Ebene einheitlich in Richtung Abstraktionen fließen sollten.
Das heißt, Abhängigkeiten sollten einheitlich in Richtung "Stabilität" fließen, und genauer gesagt, Abhängigkeiten sollten in Richtung von Paketen mit mehr eingehenden Abhängigkeiten als ausgehenden Abhängigkeiten fließen, und außerdem sollten Abhängigkeiten in Richtung Abstraktionen fließen. Der Grundgedanke dahinter ist, dass Abstraktionen Raum zum Atmen bieten, um einen Subtyp durch einen anderen zu ersetzen, und den konkreten Teilen, die die Schnittstelle implementieren, diesen Grad an Flexibilität bieten, um sich zu ändern, ohne die eingehenden Abhängigkeiten von dieser abstrakten Schnittstelle zu zerstören.
Nun, ich stimme Martin hier zumindest für meine Domain nicht zu, und hier muss ich eine neue Definition von "Stabilität" einführen, wie in "Fehlende Gründe für eine Änderung". In diesem Fall würde ich sagen, Abhängigkeiten sollten in Richtung Stabilität fließen, aber abstrakte Schnittstellen helfen nicht, wenn abstrakte Schnittstellen instabil sind (nach meiner Definition von "instabil", da sie dazu neigen, wiederholt geändert zu werden, nicht Martins). Wenn die Entwickler die Abstraktionen nicht korrigieren können und Clients ihre Meinung wiederholt so ändern, dass abstrakte Versuche, die Software zu modellieren, unvollständig oder ineffektiv sind, profitieren wir nicht mehr von der erweiterten Flexibilität abstrakter Schnittstellen, um das System vor kaskadierenden Änderungen zu schützen, die die Abhängigkeit aufheben . In meinem persönlichen Fall habe ich ECS-Engines gefunden, wie sie in AAA-Spielen zu finden sind.am konkretesten : in Richtung Rohdaten, aber solche Daten sind sehr stabil (wie in "Es ist unwahrscheinlich, dass sie jemals geändert werden müssen"). Ich habe oft festgestellt, dass die Wahrscheinlichkeit, dass zukünftige Änderungen erforderlich sind, eine nützlichere Messgröße ist als das Verhältnis von efferenten zu Gesamtkopplungen bei der Steuerung von SE-Entscheidungen.
Daher würde ich DIP ein wenig ändern und einfach sagen: "Abhängigkeiten sollten zu Komponenten fließen, bei denen die geringste Wahrscheinlichkeit besteht, dass weitere Änderungen erforderlich sind", unabhängig davon, ob es sich bei diesen Komponenten um abstrakte Schnittstellen oder Rohdaten handelt. Für mich ist nur die Wahrscheinlichkeit von Bedeutung, dass direkte Änderungen erforderlich sind. Abstraktionen sind in diesem Kontext der Stabilität nur dann nützlich, wenn etwas, indem es abstrakt ist, diese Wahrscheinlichkeit verringert.
In vielen Kontexten kann dies bei anständigen Ingenieuren und Kunden der Fall sein, die die Anforderungen der Software im Voraus antizipieren und stabile (wie in unveränderlichen) Abstraktionen entwerfen, während diese Abstraktionen ihnen den nötigen Freiraum bieten, um konkrete Implementierungen auszutauschen. In einigen Bereichen sind die Abstraktionen jedoch möglicherweise instabil und können unzureichend sein, während die für die Engine erforderlichen Daten möglicherweise viel einfacher vorherzusehen und im Voraus stabil zu machen sind. In diesen Fällen kann es unter dem Gesichtspunkt der Wartbarkeit (der einfachen Änderung und Erweiterung des Systems) tatsächlich vorteilhafter sein, dass Abhängigkeiten eher zu Daten als zu Abstraktionen fließen. In einem ECS sind die instabilsten Teile (wie bei Teilen, die am häufigsten geändert werden) normalerweise die Funktionen, die sich in Systemen befinden (
PhysicsSystem
z. B.), während die stabilsten Teile (die am wenigsten wahrscheinlich geändert werden) die Komponenten sind, die nur aus Rohdaten (MotionComponent
z. B.) bestehen, die alle Systeme verwenden.quelle