Gibt es irgendwelche signifikanten Nachteile, abhängig von Abstraktionen?

8

Nachdem ich dieses Wiki über das Stable Abstractions Prinzip (SAP) gelesen habe, habe ich mich gefragt, ob irgendjemand irgendeinen Nachteil kennt eher Abstraktionen als Betons (ich vermute, das überwiegt die Vorteile).

Die SAP sagt, je stabiler ein Paket, desto abstrakter sollte es sein. Dies bedeutet, dass, wenn ein Paket weniger stabil ist (sich eher ändert), es konkreter sein sollte. Was ich nicht wirklich verstehe ist, warum dies der Fall sein sollte. Sicher sollten wir in allen Fällen unabhängig von der Stabilität auf Abstraktionen angewiesen sein und die konkrete Umsetzung verbergen.

    
SteveCallender 09.11.2015, 09:25
quelle

3 Antworten

4
Robert C. Martin hatte immer eine ziemlich obskure Art, Dinge zu beschreiben. Seine Punkte sind immer sehr gut, erfordern aber ein wenig Entschlüsselung - "afferent vs. efferent coupling" , ugh! Eine andere Sache über die Art, wie Martin schreibt, ist, dass es immer irgendwie verschwommen zwischen deskriptiven und präskriptiven ("würde" oder "sollte"?)

"Stabilität"

Zuerst ist es wichtig zu verstehen, wie Martin "Stabilität" definiert. Er definiert es in Bezug auf afferente und efferente Kopplungen, die eine Stabilitätsmetrik ergeben:

%Vor%

"Afferent" und "efferent" sind solche obskuren Begriffe. Der Einfachheit halber verwenden wir "ausgehende Abhängigkeiten" anstelle von "efferent-Kopplungen" und "eingehende Abhängigkeiten" für "afferente Kopplungen". So haben wir das:

%Vor%

Es ist sehr von der Wahrscheinlichkeit der Veränderung losgelöst und hat alles mit der Schwierigkeit der Veränderung zu tun. So verwirrend es auch sein mag, mit dieser Definition könnte sich ein "stabiles" Paket immer ändern (es wäre natürlich schlecht und wirklich schwer zu verwalten).

Wenn Sie einen Fehler durch Division durch Null mit der obigen Formel erhalten, wird Ihr Paket weder verwendet noch verwendet.

Prinzip der stabilen Abhängigkeiten

Um Martins Standpunkt zu SAP im Kontext zu verstehen, ist es einfacher, mit SDP (Stable Dependencies Principle) zu beginnen. Es heißt:

  

Die Abhängigkeiten zwischen Paketen sollten in Richtung der   Stabilität der Pakete. Ein Paket sollte nur von Paketen abhängen   das sind stabiler als es ist.

Das ist ziemlich einfach zu verstehen. Die Kosten für die Änderung eines Designs fallen mit der Anzahl (und Komplexität) eingehender Abhängigkeiten zu ihm zusammen. Wahrscheinlich kann jeder, der in einer großen Codebase gearbeitet hat, diese ziemlich schnell erkennen, wo eine zentrale Änderung des Designs dazu führen könnte, 10.000 wirklich komplexe Teile in der Codebasis zu durchbrechen.

Also sollten die Abhängigkeiten zu den Teilen fließen, die unveränderlich, fest verwurzelt, unerschütterlich sind, wie ein Baum, der von seinen Blättern zu seinen Wurzeln hinabfließt.

Die Stabilitätsmetriken, die die Wurzeln haben sollten, sind auf zero efferent couplings (null ausgehende Abhängigkeiten) reduziert. Das heißt, dieses stabile "root" -Paket sollte von nichts anderem abhängen. Mit anderen Worten, es sollte völlig unabhängig von der Außenwelt sein. Dies ist die Eigenschaft, die "maximale Stabilität" nach Martins Maßstäben definiert: totale Unabhängigkeit.

%Vor%

Wie können wir angesichts dieser Art von völlig unabhängigem, ultrastabilem Root-Design noch ein Maß an Flexibilität zurückgewinnen, bei dem wir die Implementierung einfach erweitern und ändern können, ohne die Schnittstelle / das Design zu beeinträchtigen? Und hier kommen Abstraktionen ins Spiel.

Prinzip der stabilen Abstraktionen

Abstraktionen ermöglichen uns, die Implementierung von der Schnittstelle / dem Design zu entkoppeln.

Und so kommt hier das Prinzip der stabilen Abstraktion:

  

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

Die Idee ist, dass diese zentralen Root-Designs, wie von SDP angegeben, ultrastabil sind, während sie gleichzeitig einen Grad an Flexibilität für Änderungen beibehalten, die das Kerndesign durch Abstraktion nicht beeinflussen.

Als einfaches Beispiel betrachten Sie ein Software-Entwicklungskit, das im Kern einer Engine steht und von Plugin-Entwicklern weltweit verwendet wird. Per Definition müsste dieses SDK aufgrund der Kombination zahlreicher eingehender Abhängigkeiten (alle diese Plugin-Entwickler verwenden es) gegen minimale oder keine ausgehenden Abhängigkeiten (das SDK hängt von wenig anderem) ein sehr stabiles Design haben. Dieses Prinzip würde nahe legen, dass seine Schnittstellen abstrakt sein sollten, um ein Höchstmaß an Flexibilität für Veränderungen zu bieten, ohne das stabile Design zu beeinträchtigen.

"Moderat abstrakt" könnte hier eine abstrakte Basisklasse sein. "Maximal abstrakt" wäre eine reine Schnittstelle.

Beton

Auf der anderen Seite, mit dem abstrakten ist eine Notwendigkeit für den konkreten. Sonst gäbe es nichts, was die Implementierung für eine Abstraktion bereitstellen könnte. Dieser Grundsatz legt also nahe, dass die Betonteile die instabilen Teile sein sollten (würden?). Stellt man sich dies als einen Baum vor (invertiert vom üblichen Programmierbaum) mit Abhängigkeiten, die von Blatt zu Wurzel abwärts fließen, sollten die Blätter am konkretesten sein, die Wurzeln sollten die abstraktesten sein.

Die Blätter würden normalerweise die meisten abhängigen Abhängigkeiten haben (viele Abhängigkeiten zu Dingen außerhalb - zu all diesen Zweigen und Wurzeln), während sie null eingehende Abhängigkeiten hätten (nichts würde von ihnen abhängen). Die Wurzeln wären umgekehrt (alles hängt von ihnen ab, sie hängen von nichts ab).

So habe ich Martins Beschreibungen verstanden. Sie sind schwer zu verstehen und ich kann in einigen Teilen abweichen.

  

Sicher in allen Fällen unabhängig von der Stabilität sollten wir davon abhängig sein   auf Abstraktionen und verstecke die konkrete Implementierung?

Vielleicht denkst du mehr an Entitäten. Eine abstrakte Schnittstelle für eine Entität würde immer noch eine konkrete Implementierung erfordern. Der Betonteil kann instabil sein und wäre ebenfalls leichter zu ändern, da nichts direkt davon abhängt (keine afferenten Kupplungen). Der abstrakte Teil sollte stabil sein, da viele potentiell davon abhängen könnten (viele eingehende Abhängigkeiten, wenige oder keine ausgehenden Abhängigkeiten), und so wäre es schwierig zu ändern.

Zur gleichen Zeit, wenn Sie sich auf ein abhängigeres Paket wie das Anwendungspaket hocharbeiten, wo Sie Ihren Haupteingangspunkt für Ihre Anwendung haben, wo alles zusammengebaut wird, würde das häufig dazu führen, dass alle Schnittstellen hier abstrakt sind Schwierigkeiten der Veränderung, und würde immer noch die Notwendigkeit übertragen, eine konkrete (instabile) Implementierung woanders zu haben. Irgendwann in einer Codebasis muss es Abhängigkeiten zu konkreten Teilen geben, sei es nur um die passende konkrete Implementierung für eine abstrakte Schnittstelle auszuwählen.

Zur Zusammenfassung oder nicht zur Zusammenfassung

  

Ich habe mich gefragt, ob irgendjemand einen Nachteil kennt, von dem er abhängt   eher Abstraktionen als Betons (ich nehme an, das überwiegt das   Vorteile).

Leistung kommt in den Sinn. Typischerweise haben Abstraktionen irgendeine Art von Laufzeitkosten in der Form von dynamischer Verteilung, die z. B. anfällig für Verzweigungsfehlvorhersagen werden. Ein Großteil von Martins Schrift dreht sich um klassische objektorientierte Paradigmen. Darüber hinaus möchte OOP im Allgemeinen Dinge auf der Ebene der singulären Entity modellieren. Auf der extremen Ebene könnte es wünschenswert sein, ein einzelnes Pixel eines Bildes zu einer abstrakten Schnittstelle mit eigenen Operationen zu machen.

In meinem Bereich tendiere ich dazu, Entity-Komponenten-Systeme mit einer datenorientierten Design-Denkweise zu verwenden. Diese Art bringt die klassische OOP-Welt auf den Kopf. Strukturen sind oft so konzipiert, dass sie Daten für mehrere Entitäten gleichzeitig mit einer Design-Denkweise sammeln, die nach einem optimalen Speicherlayout sucht (Design für die Maschine und nicht logisch für den Menschen). Entitäten werden als Sammlungen von Komponenten entworfen, und Komponenten werden als Rohdaten unter Verwendung einer datenorientierten Denkweise modelliert. Schnittstellen werden immer noch abstrakt für Systeme, die Komponenten verarbeiten, aber die Abstraktionen sind darauf ausgelegt, Dinge in großen Mengen zu verarbeiten, und die Abhängigkeiten fließen von Systemen zu zentralen Komponenten, die nicht im geringsten abstrakt sind.

Dies ist eine sehr gebräuchliche Technik, die in Game-Engines verwendet wird und bietet ein großes Potential in Bezug auf Leistung und Flexibilität. Aber es steht in starkem Kontrast zu der Art von Fokus, den Martin auf objektorientierte Programmierung legt, da es eine starke Abkehr von OOP insgesamt ist.

    
Team Upvote 01.12.2015 01:25
quelle
0

vor allem aus dem Papier, das Sie verlinken:

  

Stabilität ist kein Maß für die Wahrscheinlichkeit, dass ein Modul wird   Veränderung; es ist vielmehr ein Maß für die Schwierigkeit, ein Modul zu ändern.

Dinge, die schwer zu ändern sind (z. B. an vielen Orten) sollten abstrakt sein, um die Erweiterung einfach zu machen.

und ja, es gibt Nachteile. Es ist die Leichtigkeit der Veränderung. Es ist viel einfacher und schneller, den konkreten Code als die Abstraktion und den Code zu ändern.

  

Sicher in allen Fällen unabhängig von der Stabilität sollten wir davon abhängig sein   auf Abstraktionen und verstecke die konkrete Implementierung?

das ist wahr. aber das Abstraktionsniveau ist unterschiedlich. on-the-fly-beispiel: wenn ich dich auffordere, die länge einer quadratdiagonale zu berechnen, wirst du wahrscheinlich nur die eingebaute double sqrt(double) funktion verwenden. ist es abstrahiert? Ja. Wir wissen nicht, ob eine Newton-Methode verwendet wird oder ob sie direkt an die CPU delegiert wird.

aber was, wenn wir eine sqrt-Funktion erstellen und eine Art Physik-Berechnungsbibliothek darauf aufbauen wollen? ist die vorherige Abstraktion in diesem Fall genug? wahrscheinlich nicht, da wir vielleicht (in einer einheitlichen Weise) Matrizen, relative Fehler, willkürliche Längenzahlen, Parallelisierung für die gewünschte Anzahl von Kernen / Threads behandeln, vielleicht an GPU delegieren und es für andere Erweiterungen vorbereiten sollte, weil früher oder später jemand es tun kann will es mit NaNs und imaginären Zahlen umgehen.

also ist es immer noch sqrt-Funktion, aber Abstraktionsniveau ist ein bisschen höher. und das nur, weil viel Code davon abhängt. und welche Funktion ist einfacher zu ändern?

    
piotrek 10.11.2015 14:34
quelle
0
  

Dies bedeutet, dass wenn ein Paket weniger stabil ist (sich eher ändert)   dann sollte es konkreter sein. Was ich nicht wirklich verstehe, ist warum   Das sollte der Fall sein.

Abstraktionen sind Dinge, die in der Software schwer zu ändern sind, weil alles davon abhängt. Wenn sich Ihr Paket häufig ändert und es Abstraktionen liefert, werden Leute, die davon abhängig sind, gezwungen sein, eine große Menge ihres Codes neu zu schreiben, wenn Sie etwas ändern. Wenn Ihr instabiles Paket jedoch einige konkrete Implementierungen bereitstellt, muss nach Änderungen viel weniger Code neu geschrieben werden.

Wenn sich Ihr Paket also häufig ändert, sollte es besser Betone und keine Abstraktionen liefern. Sonst ... Wer zum Teufel wird es benutzen? ;)

    
Vladislav Rastrusny 11.11.2015 07:37
quelle