Gibt es signifikante Nachteile bei der Abhängigkeit von Abstraktionen?

9

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?

SteveCallender
quelle
Versuchen Sie, eine Komponente zu verwenden, mit der Sie vertraut sind, indem Sie nicht die darin enthaltenen Abstraktionen verwenden, sondern alles detailliert auf einer Ebene ausführen, die niedriger ist als Sie es gewohnt sind. Das gibt Ihnen einen ziemlich guten Eindruck von den Vor- und Nachteilen der Abstraktion.
Kilian Foth
2
Haben Sie den verlinkten Artikel und / oder das Buch gelesen, auf dem der Artikel basiert?
Jörg W Mittag
1
+1 Gute Frage, zumal ich nicht denke, dass die Beziehung zwischen Stabilität und Abstraktion sofort intuitiv ist. Seite 11 dieses Artikels hilft, sein Beispiel für den abstrakten Fall ist sinnvoll, aber vielleicht kann jemand ein klareres Beispiel für den konkreten Fall schreiben. Anfrage zum Abheben.
Mike unterstützt Monica
Mit welcher Problemdomäne beschäftigen Sie sich bei diesen Abstraktionen? Wie in C2 erwähnt: "Bei der Modellierung realer Domänen - der Welt der Kunden, Mitarbeiter, Rechnungen, Stücklisten, Produkte, SKUs, Gehaltsschecks usw. - sind stabile Abstraktionen möglicherweise schwer zu finden. Computerdomänen - die Die Welt der Stapel, Warteschlangen, Funktionen, Bäume, Prozesse, Threads, grafischen Widgets, Berichte, Formulare usw. ist mit größerer Wahrscheinlichkeit stabil. " und "In einigen Bereichen sind stabile Abstraktionen schwer zu bekommen." Ohne zu wissen, welches Problem Sie mit SAP lösen möchten, ist es schwierig, Ihnen eine gute Antwort zu geben.
@ JörgWMittag und Mike - Ja, ich habe den Artikel gelesen. Ich habe nur das Gefühl, dass es an einer Erklärung mangelt, warum "instabile Pakete konkret sein sollten". Auf Seite 13 dieses Artikels zeigt er eine Grafik, erklärt aber nicht zu ausführlich, warum (1,1) in der Grafik vermieden werden sollte. Ist die Idee, dass Instabilität grundsätzlich weniger afferente Abhängigkeiten bedeutet und daher keine Notwendigkeit besteht, Abstraktion zu verwenden? Wenn ja ... ist es nicht empfehlenswert, die Abstraktion trotzdem zu verwenden, nur für den Fall, dass sich die Stabilität mit den Anforderungen ändert.
SteveCallender

Antworten:

7

Stellen Sie sich Ihre Pakete als API vor, um das Beispiel aus dem Papier zu übernehmen und Definitionen für Readermit string Reader.Read()und Writermit void Writer.Write(string)als Ihre abstrakte API zu verwenden.

Sie können dann eine Klasse Copymit einer Methode Copier.Copy(Reader, Writer)und der Implementierung Writer.Write(Reader.Read())sowie möglicherweise einigen Überprüfungen der Integrität erstellen .

Nun machen Sie konkrete Implementierungen, zB FileReader, FileWriter, KeyboardReaderund DownloadThingsFromTheInternetReader.

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üssen Copier, FileReader, KeyboardReaderund DownloadThingsFromTheInternetReader.

Dies ist der Grund für das Prinzip der stabilen Abstraktion: Machen Sie Ihre Konkretisierungen weniger stabil als die Abstraktionen.

Residuum
quelle
1
Ich stimme mit allem überein, was Sie sagen, aber ich glaube, dass sich die Definition der Stabilität durch den Autor und Ihre unterscheidet. Sie behandeln Stabilität als Änderungsbedarf. Der Autor sagt: "Stabilität ist kein Maß für die Wahrscheinlichkeit, dass sich ein Modul ändert, sondern ein Maß für die Schwierigkeit, ein Modul zu ändern." Meine Frage ist also eher, warum es für Pakete, die leicht geändert werden können, vorteilhafter ist, konkreter als abstrakt zu sein.
SteveCallender
1
@SteveCallender Es ist ein subtiler Unterschied: Die Definition des Autors von "Stabilität" ist das, was die meisten Leute als "Bedürfnis nach Stabilität" bezeichnen. Je mehr Module von einem Modul abhängen, desto "stabiler" muss ein Modul sein.
Residuum
6

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).

Gepunktet
quelle
1
Ich stimme YAGNI eher zu, aber ich wundere mich über Ihr Beispiel. Wiederholen Sie nie jeden Code in den verschiedenen Geräten? Es fällt mir schwer zu glauben, dass es auf den Geräten derselben Firma keinen gemeinsamen Code gibt. Wie gefällt es Clients, wenn Sie keine Fehler in ihrer Firmware beheben? Wollen Sie damit sagen es nie Bugs sind, je ? Wenn Sie denselben Code haben, der in 4 verschiedenen Implementierungen fehlerhaft ist, müssen Sie den Fehler viermal beheben, wenn er nicht in einem gemeinsamen Modul enthalten ist.
Fuhrmanator
1
Der gemeinsame Code von @Fuhrmanator unterscheidet sich von Abstraktionen. Allgemeiner Code kann nur eine Hilfsmethode oder Bibliothek bedeuten - es sind keine Abstraktionen erforderlich.
Eilon
@Fuhrmanator Natürlich haben wir gemeinsamen Code in Bibliotheken, aber wie Eilon sagte, hängt nicht alles von Abstraktionen ab (einige Teile jedoch). Ich habe nie gesagt, dass es nie Fehler gibt, ich habe gesagt, dass sie nicht gepatcht werden können (aus Gründen, die außerhalb des Rahmens der OP-Frage liegen).
Spotted
@Eilon mein Kommentar war über Modularität wird nicht immer benötigt (keine Abstraktionen).
Fuhrmanator
@Spotted Kein Problem, weil ich nicht patchen kann. Es ist nur ein ziemlich spezifisches Beispiel und nicht typisch für die meisten Softwareprogramme.
Fuhrmanator
6

Ich denke, Sie sind vielleicht verwirrt von dem Wort Stall , das Robert Martin gewählt hat. Hier beginnt meiner Meinung nach die Verwirrung:

Dies bedeutet, dass ein Paket, das weniger stabil ist (sich eher ändert), konkreter sein sollte.

Wenn Sie den Originalartikel durchlesen , werden Sie sehen (Hervorhebung von mir):

Die klassische Definition des Wortes Stabilität lautet: "Nicht leicht zu bewegen." Dies ist die Definition, die wir in diesem Artikel verwenden werden. Das heißt, Stabilität ist kein Maß für die Wahrscheinlichkeit, dass sich ein Modul ändert. Vielmehr ist es ein Maß für die Schwierigkeit , ein Modul zu wechseln .

Es ist klar, dass Module, die schwieriger zu ändern sind, weniger volatil sind. Je schwieriger das Modul zu ändern ist, dh je stabiler es ist, desto weniger flüchtig wird es sein.

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 ):

  • Obligatorisch - das heißt, andere Klassen hängen von dieser Klasse ab, sodass sie sich nicht ändern sollten.
  • Beholden - ibid.
  • Eingeschränkt - Die Verpflichtungen dieser Klasse schränken ihre Möglichkeiten zur Änderung ein.

Fügen Sie diese Definitionen in die Anweisung ein

Je stabiler ein Paket ist, desto abstrakter sollte es sein

  • Je verbindlicher ein Paket ist, desto abstrakter sollte es sein
  • desto mehr beholden ein Paket desto abstrakter sollte es sein
  • Je eingeschränkter ein Paket ist, desto abstrakter sollte es sein

Lassen Sie uns das Stable Abstractions Principle (SAP) zitieren und die verwirrenden Wörter stabil / instabil hervorheben:

Pakete, die maximal stabil sind, sollten maximal abstrakt sein. Instabile Pakete sollten konkret sein. Die Abstraktheit eines Pakets sollte im Verhältnis zu seiner Stabilität stehen .

Klarstellung ohne diese verwirrenden Worte:

Pakete, die maximal anderen Teilen des Systems verpflichtet sind, sollten maximal abstrakt sein. Pakete , die sich problemlos ändern können, sollten konkret sein. Die Abstraktheit eines Pakets sollte proportional zu der Schwierigkeit sein, Änderungen vorzunehmen .

TL; DR

Der Titel Ihrer Frage lautet:

Gibt es signifikante Nachteile bei der Abhängigkeit von Abstraktionen?

Ich denke, wenn Sie die Abstraktionen richtig erstellen (z. B. existieren sie, weil viel Code von ihnen abhängt), gibt es keine wesentlichen Nachteile.

Fuhrmanator
quelle
0

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.

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? ;)

Vladislav Rastrusny
quelle
0

Denken Sie an Martins Stabilitätsmetrik und was er unter "Stabilität" versteht:

Instability = Ce / (Ca+Ce)

Oder:

Instability = Outgoing / (Incoming+Outgoing)

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.

Gibt es signifikante Nachteile bei der Abhängigkeit von Abstraktionen?

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 (PhysicsSystemz. B.), während die stabilsten Teile (die am wenigsten wahrscheinlich geändert werden) die Komponenten sind, die nur aus Rohdaten ( MotionComponentz. B.) bestehen, die alle Systeme verwenden.


quelle