Welchen Wert hat es, die Details durch Abstraktionen zu verbergen? Ist Transparenz nicht von Wert?

30

Hintergrund

Ich bin kein großer Fan von Abstraktion. Ich gebe zu, dass man von Anpassungsfähigkeit, Portabilität und Wiederverwendbarkeit von Schnittstellen usw. profitieren kann. Da gibt es einen echten Vorteil, und ich möchte das nicht in Frage stellen, also lassen Sie uns das ignorieren.

Es gibt den anderen großen "Vorteil" der Abstraktion, nämlich die Implementierungslogik und Details vor den Benutzern dieser Abstraktion zu verbergen. Das Argument ist, dass Sie die Details nicht kennen müssen und sich an dieser Stelle auf ihre eigene Logik konzentrieren sollten. Sinnvoll in der Theorie.

Wenn ich jedoch große Unternehmensanwendungen gewartet habe, muss ich immer mehr Details wissen. Es wird zu einem gewaltigen Problem, mit jedem Schritt tiefer und tiefer in die Abstraktion einzutauchen, nur um genau herauszufinden, was etwas tut. Das heißt, Sie müssen ungefähr 12 Mal "open declare" ausführen, bevor Sie die verwendete gespeicherte Prozedur finden.

Diese Mentalität, Details zu verbergen, scheint nur im Weg zu sein. Ich wünsche mir immer transparentere Schnittstellen und weniger Abstraktion. Ich kann Quellcode auf hoher Ebene lesen und weiß, was er tut, aber ich werde nie wissen, wie er es tut, wenn es so ist, was ich wirklich wissen muss.

Was ist hier los? Wurde jedes System, an dem ich jemals gearbeitet habe, schlecht entworfen (zumindest aus dieser Perspektive)?

Meine Philosophie

Bei der Entwicklung von Software versuche ich, einer Philosophie zu folgen, die meiner Meinung nach eng mit der ArchLinux-Philosophie verbunden ist :

Arch Linux behält die inhärenten Komplexitäten eines GNU / Linux-Systems bei, während es gut organisiert und transparent bleibt. Entwickler und Benutzer von Arch Linux glauben, dass der Versuch, die Komplexität eines Systems zu verbergen, tatsächlich zu einem noch komplexeren System führt und daher vermieden werden sollte.

Und deshalb versuche ich nie, die Komplexität meiner Software hinter Abstraktionsebenen zu verbergen. Ich versuche die Abstraktion zu missbrauchen, nicht ein Sklave dafür zu werden.

Frage im Herzen

  1. Ist es sinnvoll, die Details zu verbergen?
  2. Opfern wir nicht Transparenz?
  3. Ist diese Transparenz nicht wertvoll?
user606723
quelle
7
Abstraktion kann in Form von schlechtem Design missbraucht werden. Das heißt aber nicht, dass Abstraktion im Prinzip nicht wertvoll ist.
Bernard
4
Ich denke, es gibt eine gute Frage, aber sie liest sich wie eine Schimpfworte gegen die Abstraktion. Könntest du das vielleicht nicht betonen und deine eigentliche Frage mehr zur Sprache bringen?
PersonalNexus
4
Sind Sie sicher, dass Sie die richtige Definition für "Details verbergen" verwenden? In diesem Zusammenhang geht es darum, die Kopplung zu verringern , und nicht darum, Sie daran zu hindern, das Innenleben von etwas zu lernen.
Andres F.
24
Wenn Sie nicht gerne mit einem Voltmeter und einem Oszilloskop an Ihrem Schreibtisch programmieren, programmieren Sie gegen nichts als Abstraktionen über Abstraktionen über Abstraktionen. Hat es für Sie einen Wert, das Detail zu verbergen, dass Sie tatsächlich nicht Bits, sondern Spannungen manipulieren? Opfert dies die Transparenz? Ist diese Transparenz wertvoll?
Eric Lippert
8
Ich denke, was Sie Probleme haben, ist keine Abstraktion, sondern leere Indirektionsebenen, die eigentlich nichts abstrahieren. Ja, diese sind häufig in großen Unternehmenssystemen zu finden und nein, sie sind nicht gut.
Michael Borgwardt

Antworten:

47

Der Grund für das Ausblenden der Details besteht nicht darin, die Details verborgen zu halten. Dies soll es ermöglichen, die Implementierung zu ändern, ohne abhängigen Code zu beschädigen.

Stellen Sie sich vor, Sie haben eine Liste von Objekten und jedes Objekt hat eine NameEigenschaft und andere Daten. Häufig müssen Sie ein Element in der Liste finden, das Namemit einer bestimmten Zeichenfolge übereinstimmt.

Die naheliegende Möglichkeit besteht darin, jedes Element einzeln zu durchlaufen und zu prüfen, ob Namedie Zeichenfolge übereinstimmt. Wenn Sie jedoch feststellen, dass dies viel zu lange dauert (wie bei mehreren tausend Einträgen in der Liste), möchten Sie diese möglicherweise durch eine Suche im String-Objekt-Wörterbuch ersetzen.

Wenn alle Ihre Suchvorgänge durch Abrufen der Liste und Durchlaufen der Liste durchgeführt wurden, müssen Sie eine große Menge an Arbeit leisten, um dies zu beheben. Es ist noch schwieriger, wenn Sie sich in einer Bibliothek befinden und diese von Drittanbietern verwendet wird. Sie können nicht rausgehen und ihren Code reparieren !

Wenn Sie jedoch eine FindByNameMethode zur Verkapselung des Vorgangs der Namenssuche hatten, können Sie einfach die Art und Weise der Implementierung ändern und den gesamten Code, der sie aufruft, weiterhin verwenden. Das ist der wahre Wert von Abstraktion und Einkapselung.

Mason Wheeler
quelle
Ich stimme dem zu, aber das war nicht der Punkt der Frage. Ich kann völlig zustimmen, dass eine Methode zur Kapselung bestimmter Funktionen nützlich ist, aber ich bin der Meinung, dass dies nicht immer das Motiv ist. Liege ich falsch?
user606723
3
@ User606723: Es sollte sein. Das heißt nicht, dass es immer so ist . Einige Leute verstehen den Punkt nicht und stapeln einfach mehr Schichten auf mehr Schichten und verwandeln die Dinge in ein Chaos. Aus diesem Grund raten erfahrene Programmierer neueren Entwicklern, eine neue Technologie oder Technik erst dann anzuwenden, wenn sie sie verstehen.
Mason Wheeler
3
@ user606723: Transparenz fördert enge Kopplung, also vielleicht nicht immer schlecht, ist es aber meistens.
Malfist
3
Das Problem, das Mason beschreibt, wenn Menschen in Form von Frachtkult-Programmen auf Schichten stapeln, ist, warum ich zu viel Vererbung verdächtige und warum Komposition gegenüber Vererbung bevorzugt werden sollte. Scheint vor allem für Java-Programmierer ein Problem zu sein.
Am
2
Nein, enge Kopplung ist nicht immer schlecht. Es ist häufig schlecht, aber wenn man große Anstrengungen unternimmt, um dies zu vermeiden, können noch schlimmere Probleme auftreten, wie z. B. Softcodierung.
Mason Wheeler
16

Ich habe gerade den Abschnitt in Code Complete über Abstraktion durchgelesen, in dem sich die meisten dieser Quellen befinden.

Der Punkt der Abstraktion besteht darin, die Notwendigkeit zu beseitigen, zu fragen, wie dies implementiert wird. Wenn Sie anrufen user.get_id(), wissen Sie, dass idSie zurückkehren werden. Wenn Sie fragen müssen "Wie bekommt das die Benutzer-ID?" Dann brauchen Sie wahrscheinlich entweder kein idoder geben get_id()etwas zurück, das unerwartet und schlecht designt ist.

Sie verwenden die Abstraktion, um Folgendes zu entwerfen:

a house with doors and windows

nicht entwerfen

a box with four walls,
    with 3 holes,
        two of which fit panes of glass surrounded by wood frames,
        one that fits a large plank of wood with hinges and a metal knob,
etc.
TorelTwiddler
quelle
Ich spreche nicht über diese Schnittstellen. Diese Schnittstellen sind in Ordnung. Ich spreche von riesigen komplexen Systemen, die hinter vielen verschiedenen Abstraktionen segmentiert sind.
user606723
9
@ user606723 Dann geht es bei Ihrer Frage eher um überkomplexes Design als um Abstraktionen
Andres F.
3
+1 für Code abgeschlossen. Es geht sowohl darum, warum Abstraktion für das Design notwendig ist , als auch warum die falsche Abstraktionsebene das Design behindert. Um Ihr Beispiel weiter zu führen, wenn ich im Zoning-Büro arbeite, möchte ich an Ihre Häuser denken, ohne die Details zu verfälschen. Aber wenn Sie wie ich über Häuser nachdachten, konnten Sie keines bauen.
Spencer Rathbun
Diese Frage sollte geschlossen oder der Titel geändert werden
Jake Berger
8

Ist es sinnvoll, die Details zu verbergen?

Ja. Durch die Präsentation von Abstraktionen können wir auf einer höheren Ebene denken und programmieren.

Stellen Sie sich vor, Sie modellieren physikalische Systeme ohne Analysis oder Matrixalgebra. Das ist völlig unpraktisch. In ähnlicher Weise können wir interessante Probleme nicht lösen, wenn wir nur auf skalarer Ebene programmieren können. Selbst relativ einfache Webanwendungen können stark von Abstraktionen wie Tag-Bibliotheken profitieren. Es ist viel einfacher, ein Tag einzufügen, das "Adresseneingabefelder" bedeutet, als wiederholt vier Textfelder und ein Auswahlfeld zu erstellen. Und wenn Sie sich für eine Expansion nach Übersee entscheiden, können Sie einfach die Tag-Definition ändern, anstatt jedes Formular für die Bearbeitung internationaler Adressen zu korrigieren. Die effektive Nutzung der Abstraktion macht einige Programmierer zehnmal so effektiv wie andere.

Menschen haben ein begrenztes Arbeitsgedächtnis. Durch Abstraktion können wir über große Systeme nachdenken.

Aren't we sacrificing transparency?

Nein. Wenn keine Abstraktionen verwendet werden, wird der Zweck einer Softwarekomponente in wiederholten Details vergraben. Entwickler verbringen ihre Tage damit, Code wie folgt zu durchsuchen:

for (i = 0; i < pilgrim.wives.size(); ++i) {
  wife = pilgrim.wives[i];
  for (j = 0; j < wife.sacks.size(); ++j) {
     sack = wife.sacks[i];
     for (k = 0; j < sack.cats.size(); ++j) {
        cat = sack.cats[k];
        for (m = 0; m < cat.kits.size(); ++m) {
           ++count;
        }
     }
  }
}

und denken "oh ja, noch eine vierstufige Schleife über die Kits", anstatt zu sehen

pilgrim.kits.each { ++count; }

Ist diese Transparenz nicht wertvoll?

Wie Sie bereits betont haben, ist die Indirektion mit Kosten verbunden. Es hat keinen Sinn, Ebenen "nur für den Fall" zu erstellen. Verwenden Sie Abstraktion, um Doppelarbeit zu reduzieren und Code zu klären.

Kevin Cline
quelle
7

Wenn Leute sagen, dass Abstraktionen Implementierungsdetails verbergen, meinen sie nicht wirklich "verstecken" in dem Sinne, dass es schwierig ist, sie zu finden. Was sie bedeuten, sind separate Implementierungsdetails von der öffentlichen Schnittstelle, um die Schnittstelle einfach, übersichtlich und verwaltbar zu halten. So wie ein Auto die meisten wichtigen Teile "verbirgt" und nur eine relativ rudimentäre Reihe von Bedienelementen zur Verfügung stellt, "verbirgt" ein Softwaremodul den größten Teil seiner Funktionalität tief im Inneren und bietet nur eine begrenzte Anzahl von Zugriffsmethoden Fahr es. Stellen Sie sich ein Auto vor, in dem Sie alle Innenteile des Motors manuell bedienen mussten (und es gibt verdammt viele davon). Es würde Ihnen wirklich schwer fallen, den Verkehr im Auge zu behalten und den Weg zu finden.

Die Benutzeroberfläche einfach zu halten, ist jedoch nicht nur eine ästhetische Sache. Es kann den Unterschied zwischen einem erfolgreichen Projekt und einem Todesmarsch ausmachen. Lassen Sie uns für eine Minute den Anwalt des Teufels spielen; Stellen Sie sich ein Softwareprojekt ohne jegliche Abstraktion vor. Wenn Sie einen Wert beibehalten müssen, verwenden Sie eine globale Variable. Wenn Sie Funktionen mehrmals verwenden müssen, kopieren Sie sie und fügen Sie sie ein. Wenn Sie zwei verschiedene Versionen eines bestimmten Codeabschnitts benötigen, kopieren und fügen Sie ihn in eine ifAnweisung ein und ändern beide Zweige. Technisch gesehen funktioniert es, aber ein paar Monate später werden Sie mit ein paar wirklich schlimmen Problemen kämpfen:

  • Wenn Sie einen Fehler finden und beheben, ist es wahrscheinlich, dass er auch in anderen durch Kopieren eingefügten Instanzen ähnlichen Codes vorhanden ist. Neben der Suche und Behebung des Fehlers müssen Sie also auch nach anderen Vorkommnissen suchen und diese beheben.
  • Um einen Fehler zu finden oder eine Änderung zu implementieren, muss ein Wartungsprogrammierer in der Lage sein, den entsprechenden Code zu verstehen. Die Schwierigkeit steigt mit der Größe des entsprechenden Codeabschnitts, aber noch mehr mit dessen Umfang. Es ist machbar, ein halbes Dutzend Variablen im Kopf zu behalten, während Sie sich gedanklich durch Code bewegen. Aber wenn Sie ein paar hundert davon haben, ist Ihre Produktivität stark beeinträchtigt (ich vergleiche den Denkprozess gerne mit einem Programm, dem der physische Arbeitsspeicher ausgeht und das in die Auslagerungsdatei eintauchen muss, anstatt den Code fließend auf einmal zu lesen) muss der Programmierer hin und her springen, um nachzuschlagen).
  • Der Umfang eines Codeteils wirkt sich auch auf die Größe der Codebasis aus, die durchsucht werden muss, um den Fehler zu finden. Wenn Sie eine Zehn-Zeilen-Funktion mit zwei Parametern und keinen globalen Werten haben und die Werte der Eingabe und der Zeile kennen, bei der sie abstürzt, ist das Auffinden des Fehlers normalerweise trivial und erfordert oft nichts weiter als das Betrachten des Codes. Wenn es sich um ein paar hundert Zeilen, zwanzig Parameter, fünfzehn Globale handelt und ein paar andere Funktionen ähnlicher Art aufruft, werden Sie ernsthafte Schmerzen haben.
  • Ohne eine ordnungsgemäße Abstraktion kann sich jede Änderung möglicherweise auf große Teile der Codebasis auswirken, da praktisch alles vom zu ändernden Code abhängen kann. Ein typisches Symptom bei solchen Codebasen ist, dass Sie eine kleine, scheinbar unschuldige Änderung vornehmen und plötzlich ein völlig unabhängiges Merkmal ausfällt. Mit der Abstraktion können Sie den Schaden begrenzen, den eine Änderung anrichten kann, und die Auswirkungen vorhersehbarer machen. Wenn Sie den Namen eines privaten Feldes ändern, müssen Sie nur eine Quelldatei überprüfen. Wenn Sie den Namen einer globalen Variablen ändern, müssen Sie die gesamte Codebasis durchlaufen.

In einer schlecht abstrahierten Codebasis nimmt die Auswirkung in der Regel exponentiell mit der Größe der Codebasis zu, dh das Hinzufügen einer konstanten Codemenge erhöht den Wartungsaufwand um einen konstanten Faktor. Erschwerend kommt hinzu, dass das Hinzufügen von mehr Programmierern zu einem Projekt die Produktivität nicht linear, sondern bestenfalls logarithmisch steigert (denn je größer Ihr Team ist, desto mehr Aufwand ist für die Kommunikation erforderlich).

tdammers
quelle
2

Ich denke, Sie sollten verstehen, wie es bei Bedarf funktioniert. Sobald Sie festgestellt haben, dass es das tut, was Sie erwartet haben, können Sie beruhigt sein. Ich hätte nie gedacht, dass das Ziel darin besteht, es für immer und ewig zu verstecken.

Sobald Sie einen Wecker auf eine Uhr gestellt haben, von der Sie sicher sind, dass sie funktioniert, können Sie ein wenig schlafen, wenn Sie wissen, dass sie zur richtigen Zeit losgeht. Eine Stunde zu früh aufzustehen, damit Sie sehen können, wie die Sekunden vergehen, ist eine Verschwendung.

JeffO
quelle
1
Sicher, aber ich werde nicht oft gebeten, die Funktionsweise meines Weckers zu ändern oder andere Personen zu veranlassen, die Funktionsweise meines Weckers zu ändern, ohne vollständig über die Änderungen informiert zu sein.
user606723
1
Sehen Sie die Codeänderungen für jedes Framework, das Sie jemals verwendet haben?
JeffO
1
nein, aber ich werde nicht für jedes Framework, das ich jemals verwendet habe, Codeänderungen beibehalten.
user606723
3
Sie haben JeffOs Argument ziemlich genau bewiesen: Sie sind auch nicht in der Lage, Wecker zu warten. Wenn Sie eines kaufen und es verwenden, ohne es vollständig herunterzureißen und zu analysieren, wie es funktioniert, haben Sie akzeptiert, dass das Innere für Sie abstrakt ist. Nichts sagt, dass Sie es nicht zerreißen können, um herauszufinden, wie es funktioniert, aber wie oft finden Sie es notwendig?
Blrfl
2

Um Ihre Fragen gezielt zu beantworten:

Ist es sinnvoll, die Details zu verbergen?

Ja. Wie Sie in der ersten Zeile Ihrer Frage bestätigen.

Opfern wir nicht Transparenz?

Nicht wirklich. Eine gut geschriebene Abstraktion erleichtert bei Bedarf das Verständnis der Details.

Ist diese Transparenz nicht wertvoll?

Ja. Abstraktionen sollten so entworfen und implementiert werden, dass das Verständnis der Details bei Bedarf / Wunsch erleichtert wird.

Craig
quelle
Endlich eine Antwort, die mir gefällt.
user606723
1

Ich würde sagen, dass es großartig ist, die Details zu verbergen, wenn das Versteckte funktioniert.

Nehmen wir zum Beispiel an, wir entwickeln eine Schnittstelle, die Verhalten definiert (z. B. GetListItems, SendListItem). Dies sind zwei Funktionen, die vom Benutzer über einen Klick auf eine Schaltfläche oder so initiiert werden. JETZT kann jeder Benutzer seinen eigenen "ListItemStore" haben ist auf Facebook, auf MySpace .. (zum Beispiel) .. und sagt, dass es als Benutzereigenschaft / -einstellung irgendwo in der App über Benutzereinstellungen gespeichert ist .. und sagt, dass es den App-Entwicklern möglich ist, im Laufe von weitere ListItemStore hinzuzufügen zeit (mybook, facespace, etc ..)

Jetzt gibt es viele Details, wenn Sie eine Verbindung zu Facebook herstellen und diese Elemente abrufen. Ebenso viele Details, wenn Sie eine Verbindung zu myspace herstellen.

Nachdem der anfängliche "Store Access" Code geschrieben wurde, muss er möglicherweise nicht geändert werden (in Facebooks Fall benötigen wir wahrscheinlich einen Vollzeitentwickler, um mit den Änderungen Schritt zu halten, zing ..).

Wenn Sie also den Code verwenden, sieht er ungefähr so ​​aus:

    new ItemManager(user) //passes in user, allowing class to get all user properties
    ItemManager.GetListItems()

und jetzt haben Sie die Daten des Benutzers, wo immer sie gespeichert sind, und da alles, worüber ich mir Sorgen mache, ist, die Liste der Elemente abzurufen und etwas damit zu tun Viele weitere Geschäfte sind hinzugekommen. Ich kann auf das Beantworten / Posten von Fragen im Stapel zurückkommen.

Also, alles, was dafür nötig ist, ist "versteckt" und wen interessiert es wirklich, wie es funktioniert, solange ich die richtige Liste der Gegenstände erhalte. Wenn Sie Unit-Tests haben, können Sie sich sogar leichter ausruhen, denn die Ergebnisse sollten " wurde bereits quantifiziert ..

Hanzolo
quelle
Ja, aber der Teil, der gewartet werden muss, sind niemals diese beiden Zeilen. Das ist unmöglich zu vermasseln. Es ist immer die 2., 3., 4. Ebene, die Sie ändern müssen. Ich würde zustimmen, dass dies großartig ist, wenn Sie eine API haben, der Sie vertrauen können, dass sie stabil ist , aber was ist, wenn sie aufgrund der Komplexität Ihres Geschäfts nicht stabil ist?
user606723
1
@ User606723 Wenn Sie API nicht stabil ist, dann ist es wahrscheinlich entweder unreif oder eher die falsche Abstraktion
SDG
@ user606723 - Das stimmt, und in diesen Fällen sollten Namenskonventionen selbst eine Form von Transparenz definieren, die die eigentliche Programmierlogik / -detail verbirgt. Wenn Sie jemals die eigentliche Quelle ändern müssen, sollten weitere Details und Ideen durch informative Benennung ausgedrückt werden Im Grunde genommen kann die Transparenz mit der richtigen Benennung bis zum Ende erreicht werden. Wir sollten jedoch nicht wirklich oft bis zum Ende gehen müssen.
Hanzolo
@sdg oder weil sich die Anforderungen des Systems ständig ändern. Weil sich Gesetze ändern, weil sich die APIs anderer ändern, liegt dies außerhalb unserer Kontrolle.
user606723
1
@ user606723 - Ich arbeite tatsächlich an einem SaaS-Finanzsystem und sehe die Tücken, die darin bestehen, dass ich bei jedem Veröffentlichungszyklus nicht genügend Abstraktion habe. Ein Teil davon ist auf schlechtes Design zurückzuführen, aber in der Regel ist es das Ergebnis des Verschiebens von Code dort, wo er ursprünglich nicht beabsichtigt war. Ohne diese Kommentare, Namen und Verkapselungen kann es schmerzhaft sein, die Kette entlang zu gehen . wenn alles , was ich tun musste , war Remeasurements.GetRemeasureType (Zuschuss) und dann reMeasure.PerformCalc (), die so viel schöner wäre als Überlastungen das Hinzufügen und das Lesen durch die verschiedenen logischen Zweige an der richtigen Berechnung zu gelangen oder eine neue hinzufügen
hanzolo
1

Was Sie als "Verstecken" bezeichnen, wird von vielen als Trennung von Bedenken angesehen (z. B. Implementierung vs. Schnittstelle).

Meiner Meinung nach besteht ein Hauptvorteil der Abstraktion darin, das Durcheinander nicht benötigter Details aus dem begrenzten Gehirnraum des Entwicklers zu reduzieren.

Wenn Implementierungscode verschleiert wäre, könnte ich sehen, dass dies die Transparenz behindert, aber Abstraktion ist meiner Meinung nach nur eine gute Organisation.

Somantra
quelle
1

Zunächst einmal ist alles, was über eine einzelne Maschinencodeanweisung hinausgeht, im Wesentlichen Abstraktion - eine while...doSchleife ist eine konsistente symbolische Darstellung der Vergleiche und Adressaufrufe, die erforderlich sind, um einen Befehlssatz zu wiederholen, bis eine Bedingung erfüllt ist. Ebenso ist der Typ int eine Abstraktion für die Anzahl von X Bits (abhängig von Ihrem System). Beim Programmieren dreht sich alles um Abstraktion.

Sie würden wahrscheinlich zustimmen, dass diese primitiven Abstraktionen sehr nützlich sind. Nun, so ist es, in der Lage zu sein, deine eigenen zu bauen. OOAD und OOP sind alles über.

Angenommen, Sie möchten, dass die Benutzer die Daten von einem Bildschirm in verschiedenen Formaten exportieren können: Text mit Trennzeichen, Excel und PDF. Ist es nicht praktisch, eine Schnittstelle mit dem Namen "Exporter" mit einer Exportmethode (Daten) zu erstellen, auf deren Grundlage Sie einen DelimitedTextExporter, einen ExcelExporter und einen PDFExporter erstellen können, von denen jeder weiß, wie er eine bestimmte Ausgabe erstellt? Das aufrufende Programm muss lediglich wissen, dass es die export (data) -Methode aufrufen kann, und welche Implementierung auch immer verwendet wird, wird dies tun. Wenn sich die Regeln für begrenzten Text ändern, können Sie den DelimitedTextExporter ändern, ohne sich mit dem ExcelExporter herumschlagen zu müssen, wodurch er möglicherweise beschädigt wird.

Fast alle bekannten Designmuster, die in der OO-Programmierung verwendet werden, hängen von der Abstraktion ab. Ich würde empfehlen, Freeman und Freeman's Head First Design Patterns zu lesen , um ein besseres Gefühl dafür zu bekommen, warum Abstraktion eine gute Sache ist

Matthew Flynn
quelle
1
sogar Maschinencode ist eine Abstraktion, Theres reale, physische, Prozesse unterhalb der Logik geht, auch digitale Elektronik ist eine Abstraktion über das, was wirklich passiert , wie jeder, der einen Hash Overclocking einen Chip gemacht hat bezeugen kann
jk.
Zu wahr, obwohl es auf der Ebene der Maschinenbefehle konkreter zu sein scheint.
Matthew Flynn
1

Ich glaube, ich verstehe Ihre Meinung dazu, und ich glaube, ich habe eine ähnliche Meinung.

Ich habe mit Java-Entwicklern zusammengearbeitet, die 50 Codezeilen-Klassen in 3 Klassen und 3 Interfaces umwandeln, weil es einfach zu verstehen ist. Und ich konnte es nicht ertragen.

Das Ding war furchtbar schwer zu verstehen, fast unmöglich zu debuggen und musste nie "die Implementierung wechseln".

Andererseits habe ich auch Code gesehen, in dem mehrere Objekte ein ähnliches Verhalten aufweisen und an einem Ort verwendet werden. Wenn die Methoden über eine gemeinsame Schnittstelle verfügbar gemacht worden wären, könnten häufig verwendete Sortier- / Verarbeitungsschleifen verwendet werden.

Daher profitieren Kernobjekte, die wahrscheinlich in ähnlichen Szenarien verwendet werden, normalerweise von einem gemeinsamen Verhalten, auf das über die Schnittstelle zugegriffen werden sollte. Aber das ist so ziemlich alles: Einfache Dinge zu abstrahieren, weil sie richtig sind oder es möglich machen, die Implementierung zu wechseln, ist nur eine Möglichkeit, Code chaotisch zu machen.

Andererseits bevorzuge ich längere, intelligentere Klassen gegenüber einer explosiven Anzahl kleiner Klassen mit all den Problemen des lebenslangen Managements und schwer erkennbaren Beziehungen sowie Spaghetti-Call-Diagrammen. Einige Leute werden mir nicht zustimmen.

Coder
quelle
1

Der Hauptzweck des Versteckens und Abstrahierens sollte darin bestehen, den Benutzer von der Implementierung zu entkoppeln , damit sie unabhängig voneinander geändert werden können. Wenn der Konsument mit den Implementierungsdetails gekoppelt ist, sind beide Elemente aufgrund des Umgangs mit ihren Interna in Stein gemeißelt und es wird schwieriger, neue Funktionen einzuführen oder bessere Algorithmen in der Zukunft.

Versteckte Teile der Implementierung geben Ihnen beim Schreiben eines Moduls die Gewissheit, sie ändern zu können, ohne das Risiko einzugehen, anderen Code zu beschädigen, den Sie sich nicht vorstellen können.

Ein weiterer Vorteil der Bereitstellung undurchsichtiger Schnittstellen besteht darin, dass sie die Oberfläche zwischen Subsystemen erheblich verringern. Durch die Reduzierung der Interaktionsmöglichkeiten können sie vorhersehbarer, einfacher zu testen und weisen weniger Fehler auf. Die Wechselwirkungen zwischen Modulen nehmen auch quadratisch mit der Anzahl der Module zu, sodass der Versuch, dieses Komplexitätswachstum zu kontrollieren, von Nutzen ist.


Das heißt, es ist natürlich möglich, zu viel zu verstecken und Schnittstellen zu tief zu verschachteln. Es ist die Aufgabe des Programmierers als intelligenter Mensch, das System so zu gestalten, dass es maximal nützlich ist, während gleichzeitig die Komplexität minimiert und die Wartbarkeit maximiert wird.

hugomg
quelle
0

In so vielen Fällen kann man einfach nicht brauchen , zu wissen , wie die Dinge umgesetzt werden. Ich kann fast garantieren, dass Sie Code wie diesen people.Where(p => p.Surname == "Smith")so oft am Tag schreiben, aber Sie werden kaum jemals darüber nachdenken, wie diese Where()Methode tatsächlich funktioniert. Es ist dir einfach egal - du weißt, dass diese Methode da ist und dass sie dir die gewünschten Ergebnisse bringt. Warum interessiert es Sie, wie es funktioniert?

Dies gilt auch für jede interne Software. Nur weil es nicht von Oracle, Microsoft usw. geschrieben wurde, heißt das nicht, dass Sie nach der Implementierung suchen müssen. Sie können davon ausgehen, dass eine aufgerufene Methode GetAllPeopleWithSurname(string name)eine Liste der Personen zurückgibt, die diesen Nachnamen haben. Es könnte über eine Liste iterieren, es könnte ein Wörterbuch verwenden, es könnte etwas völlig Verrücktes tun, aber das sollte Sie einfach nicht interessieren .

Es gibt natürlich eine Ausnahme von dieser Regel (es wäre keine Regel ohne sie!) Und das ist, wenn es einen Fehler in der Methode gibt. Wenn Sie im obigen Beispiel eine Liste mit 3 Personen haben und wissen, dass eine von ihnen den Nachnamen Smith hat und diese nicht in der Liste zurückgegeben werden, ist Ihnen die Implementierung dieser Methode wichtig, da sie eindeutig fehlerhaft ist .

Wenn die Abstraktion richtig durchgeführt wird, ist sie wunderbar, da Sie so alle Dinge herausfiltern können, die nicht nützlich sind, wenn Sie sie zu einem späteren Zeitpunkt lesen müssen. Vergessen Sie nicht, dass viel mehr Zeit für das Lesen von Code aufgewendet wird als für das Schreiben. Daher muss der Schwerpunkt darauf liegen, diese Aufgabe so einfach wie möglich zu gestalten. Sie denken vielleicht auch, dass Abstraktion eine Hierarchie von Objekten bedeutet, die so lang ist wie Ihr Arm, aber es kann so einfach sein, eine 100-Zeilen-Methode in 10 Methoden umzugestalten, die jeweils 10 Zeilen lang sind. Was einmal 10 Schritte war, die alle zusammen gebündelt wurden, sind jetzt 10 separate Schritte, die es Ihnen ermöglichen, direkt zu dem Ort zu gelangen, an dem sich dieser nervige Käfer versteckt.

Stuart Leyland-Cole
quelle
Aber sagen wir, Sie sind derjenige, der die Implementierung betreut. Ich meine, jemand kümmert sich um die Implementierung, und diese Person sind Sie. Sie sind zufällig auch der Benutzer der Implementierung ... Änderungen der Geschäftsanforderungen (die Sie nicht vorhersagen können) führen zu Änderungen auf vielen Ebenen. Ich denke, mein Punkt ist, dass ein Fehler nicht die einzige Ausnahme von dieser Regel ist.
user606723
Das Problem istPeopleFactory.People.Strategy.MakePeople.(CoutryLaw.NameRegistry.NameMaker.Make()) as People.Female
Coder
@ user606723 Sie müssen sich nicht ständig um jede einzelne Codezeile in Ihrer Codebasis kümmern. Wenn es einen Fehler in dieser Implementierung ist oder es ist als um neu geschrieben werden markiert up (weil es langsam ist, schlecht geschrieben oder was auch immer) und Sie werden Umschreiben es, dann kümmern Sie darüber. Andernfalls sollte es nicht im Weg sein. Möglicherweise ist Ihr Problem, dass Sie versuchen, den gesamten Code ständig zu besitzen. Sie sollten sich meiner Meinung nach nur auf den Code konzentrieren, an dem Sie zu einem bestimmten Zeitpunkt arbeiten.
Stuart Leyland-Cole
@Coder Das ist natürlich eine extreme Form der Abstraktion! Zuerst dachte ich, dass es die Art von Sache war, gegen die das OP war, aber vom Lesen des Restes ihrer Antworten, scheint es, dass sie alle Abstraktion als schlecht schlecht schlecht sehen. Deshalb habe ich versucht, die verschiedenen Abstraktionsebenen in meiner Antwort zu erklären.
Stuart Leyland-Cole
0

Abstraktionen führen dazu, dass Informationen ausgeblendet werden. Dies sollte in einer unteren Kupplung enden. Dies sollte zu weniger Änderungsrisiken führen. Dies sollte zu glücklichen Programmierern führen, die beim Berühren von Code nicht nervös werden.

Diese Ideen werden durch drei wesentliche Gesetze in der Softwarearchitektur ausgedrückt:

Simons Gesetz: "Hierarchien reduzieren die Komplexität." (Hierarchien führen Abstraktion ein)

Parnas Gesetz: "Nur was verborgen ist, kann ohne Risiko geändert werden."

Constantins Gesetz: Robuste Programme erfordern eine geringe Kopplung und eine hohe Kohäsion

ins0m
quelle
"Hierarchien reduzieren die Komplexität." - Nicht unbedingt wahr.
Coder
Keine Entwurfsmethodik ist deterministisch. Dieses Gesetz kommt nicht von IT / CS, es ist in einem viel breiteren Sinne formuliert und wird auch von Mathematik, Physik usw. erwähnt. Es ist ein gültiges Prinzip, aber niemand kann Sie daran hindern, unsinnige Hierarchien zu erstellen.
Ins0m
0

Ich bin auch im Bereich Unternehmensanwendungen tätig, und diese Frage erregte meine Aufmerksamkeit, weil ich selbst die gleiche Frage habe. Ich hatte im Laufe meiner bisherigen Karriere einige Einsichten in Bezug auf das Abstraktionsproblem, aber meine Einsicht ist keineswegs die universelle Antwort. Ich lerne weiter und höre mir neue Ideen und Gedanken an. Was ich jetzt glaube, kann sich also ändern.

Als ich eine große und komplexe Anwendung für das Gesundheitswesen unterhielt und Sie mochte, hasste ich alle Abstraktionen dort. Herauszufinden, wohin der ganze Code geht, war Schmerz im Nacken. Das Springen in verschiedenen Klassen machte mich schwindelig. Also sagte ich mir: "Abstraktion ist zum Kotzen, ich werde die Abstraktion minimieren, wenn ich etwas entwerfe."

Dann kam es zu der Zeit, dass ich eine Anwendung (relativ kleine Web-Service-Komponente) von Grund auf neu entwerfen musste. Ich erinnerte mich an all die Schmerzen und hatte ein ziemlich flaches Design der Komponente. Das Problem war, als sich die Anforderungen änderten, musste ich an vielen verschiedenen Stellen Änderungen vornehmen (die Anforderungen waren recht fließend und ich konnte nichts dagegen tun). Es war so schlimm, dass ich mein anfängliches Design und meine Neugestaltung mit Abstraktion weggeworfen habe und die Dinge besser wurden - ich musste nicht an vielen Stellen Änderungen vornehmen, als sich die Anforderungen mehr änderten.

Ich lieferte die Anwendung, saß einige Wochen da und wurde angewiesen, mit der Pflege der Anwendung zu beginnen. Ich hatte eine Weile gebraucht, ich konnte mich nicht mehr an alles erinnern, also bemühte ich mich ein wenig, meinen eigenen Code zu verstehen, und die Abstraktion half nicht.

Danach wurde ich für viele andere Projekte eingesetzt und hatte die Gelegenheit, ein bisschen mehr mit den Abstraktionsstufen zu spielen. Was ich tatsächlich finde, ist, und dies ist nur meine persönliche Meinung, Abstraktion hilft viel bei der Entwicklung, hat aber negative Auswirkungen, wenn Sie den Code nicht geschrieben haben und versuchen, alles bis auf die tiefste Ebene einer Anwendung zu verstehen. Sie werden mehr Zeit damit verbringen, durch verschiedene Klassen zu springen und zu versuchen, die Verbindungen herzustellen.

Ich habe das Gefühl, dass Abstraktion während der Entwicklungszeit so wertvoll ist, dass sich die Mühe lohnt, die wir als Betreuer beim Versuch, den Code zu verstehen, machen. Software ist vorhanden, um geschäftliche Probleme zu lösen. Geschäftliche Probleme entwickeln sich im Laufe der Zeit. Daher muss sich Software im Laufe der Zeit weiterentwickeln. Ohne Abstraktion ist die Weiterentwicklung der Software sehr schwierig. Man kann Abstraktion so gestalten, dass der Betreuer leicht durch die Codebasis navigieren kann, sobald er das Muster der Codestruktur sieht, so dass nur die anfängliche Lernkurve frustrierend ist.

Alvin
quelle
0

Wie bereits erwähnt, können Details, die sich hinter einer Abstraktion befinden, geändert werden, ohne dass dies Auswirkungen auf die Benutzer hat. Diese Idee stammt von Parnas ' On the Criteria, die bei der Zerlegung von Systemen in Module (1972) verwendet werden sollen , und steht im Zusammenhang mit der Idee der abstrakten Datentypen (ADT) und der objektorientierten Programmierung.

Etwa zur gleichen Zeit wurde Codds " Ein relationales Datenmodell für große gemeinsam genutzte Datenbanken" (1970 ) durch den Wunsch motiviert (siehe Zusammenfassung und Einführung), die interne Speicherdarstellung von Datenbanken zu ändern, ohne die Benutzer der Datenbank zu beeinträchtigen. Er hatte gesehen, wie Programmierer regelmäßig Tage damit verbracht hatten, Codeseiten zu modifizieren, um mit geringfügigen Speicheränderungen fertig zu werden.

Allerdings ist eine Abstraktion nicht sehr nützlich, wenn Sie sehen müssen, was sich darin befindet, um sie verwenden zu können. Es kann sehr schwierig sein, es gut zu gestalten. Ein Beispiel für eine gute Abstraktion ist Addition - wann mussten Sie das letzte Mal darüber nachdenken, was im Inneren passiert? (aber manchmal tust du das, zB für Überlauf).

Das wesentliche Problem (IMHO) ist, dass Sie vorhersagen müssen, was sich ändern wird und was nicht, um Module gut (im Sinne von Parnas) zu entwerfen. Die Zukunft vorauszusagen ist schwierig - aber wenn Sie viel Erfahrung mit etwas haben und es klar verstehen, können Sie ziemlich gut voraussagen. Und deshalb können Sie ein Modul (Abstraktion) entwerfen, das gut funktioniert.

Es scheint jedoch das Schicksal aller Abstraktionen - selbst der allerbesten - zu sein, dass es irgendwann unvorhergesehene (und wohl auch unvorhersehbare) Änderungen geben wird, die ein Brechen der Abstraktion erfordern. Um dies zu beheben, haben einige Abstraktionen einen Fluchtweg, über den Sie auf eine tiefere Ebene zugreifen können, wenn Sie diese wirklich benötigen.

Das alles scheint sehr negativ zu sein. Aber ich denke, die Wahrheit ist, dass wir von Abstraktionen umgeben sind, die so gut funktionieren, dass wir sie nicht bemerken oder erkennen, was sie sich verstecken. Wir bemerken nur die schlechten Abstraktionen, also haben wir einen kranken Blick auf sie.

13ren
quelle
0

Abstraktionen dienen hauptsächlich dem Nutzen ihrer Verbraucher (z. B. Anwendungsprogrammierer). Die System- (Designer-) Programmierer haben mehr zu tun, um sie schön und nützlich zu machen, weshalb gutes Design normalerweise nicht von Anfängern gemacht wird.

Vielleicht mögen Sie Abstraktionen nicht, weil sie immer komplexer werden? Vielleicht hatten die Systeme, an denen Sie gearbeitet haben, eine Abstraktionsentzündung (übermäßige Verwendung von Abstraktionen)? Sie sind kein Allheilmittel.

Die zusätzliche Arbeit und Komplexität einer nützlichen Abstraktion sollte sich auszahlen, aber es ist schwer zu wissen, sicher. Wenn Sie sich eine Abstraktion als Dreh- und Angelpunkt vorstellen, kann sich das Softwaredesign nach beiden Seiten drehen: Implementierungen der Abstraktion können geändert werden, ohne den Clientcode zu beschädigen, und / oder neue Clients können die Abstraktion problemlos wiederverwenden, um neue Dinge zu erstellen.

Sie könnten den Return-on-Investment von Abstraktionen fast messen, indem Sie zeigen, dass sie im Laufe der Zeit in eine oder beide der folgenden Richtungen "gebogen" wurden: relativ schmerzlose Implementierungsänderungen und zusätzliche neue Kunden.

Zum Beispiel: Wenn ich die Abstraktion der Socket-Klasse in Java verwende, bin ich sicher, dass mein Anwendungscode aus Java 1.2 unter Java 7 noch einwandfrei funktioniert (auch wenn sich möglicherweise die Leistung ändert). Seit Java 1.2 gab es definitiv auch viele neue Clients, die diese Abstraktion verwendeten.

Was die Unzufriedenheit mit Abstraktionen betrifft, wenn ich mit den Entwicklern gesprochen habe, die den Code hinter der Socket-Klasse gepflegt haben , dann ist ihr Leben vielleicht nicht so pfirsichfarben und rosig wie das der Kunden, die Socket zum Schreiben unterhaltsamer Anwendungen verwendeten. An der Implementierung einer Abstraktion zu arbeiten ist sicherlich mehr Arbeit als sie zu benutzen. Aber das macht es nicht schlimm.

Bei einer Top-Down-Entwurfsstrategie führt vollständige Transparenz zu einem schlechten Entwurf. Intelligente Programmierer nutzen die Informationen, die ihnen zur Verfügung stehen, in der Regel optimal, und das System ist eng miteinander verbunden. Die kleinste Detailänderung (z. B. die Änderung der Bytereihenfolge in einer Datenstruktur) in einem Modul kann Code an einer anderen Stelle beschädigen, da ein intelligenter Programmierer diese Informationen verwendet hat, um etwas Nützliches zu tun. David Parnas wies auf dieses Problem in Artikeln aus dem Jahr 1971 hin, in denen er vorschlug, Informationen in Entwürfen zu verstecken.

Ihr Verweis auf ArchLinux ist für mich sinnvoll, wenn Sie das "Innere" des Betriebssystems als die komplexe Implementierung der Abstraktion betrachten, die das Betriebssystem für die darauf ausgeführten Anwendungen darstellt. Halten Sie es einfach in den Eingeweiden der Abstraktion.

Fuhrmanator
quelle
0

Ich beantworte Ihre Frage mit einer Frage; Als Sie heute Morgen zur Arbeit fuhren (ich nehme an, Sie haben es tatsächlich getan), war es Ihnen wichtig, wie der Motor die Ventile öffnete, um Kraftstoff-Luft-Gemische einzulassen, und diese dann zündete? Nein, es ist dir egal, wie der Motor deines Autos funktioniert, wenn du die Straße runter fährst. Es ist dir wichtig, dass es funktioniert .

Angenommen, eines Tages funktioniert Ihr Auto nicht mehr. Startet nicht, wirft eine Rute, bricht einen Gürtel, pflügt sich unverschuldet unverschuldet in diese Betonbarriere, während Sie gerade eine SMS geschrieben haben. Jetzt brauchen Sie ein neues Auto (zumindest vorübergehend). Interessiert es Sie genau, wie dieses neue Auto funktioniert? Nein. Was Sie interessiert, ist erstens, dass es funktioniert und zweitens, dass Sie die gleichen Kenntnisse und Fähigkeiten nutzen können, mit denen Sie Ihr altes Auto gefahren haben, um das neue zu fahren. Idealerweise sollte es Ihnen so erscheinen, als ob sich an dem Auto, das Sie fahren, nichts geändert hat. Realistisch sollte die Art und Weise, wie dieses neue Auto funktioniert, so wenig "Überraschungen" wie möglich bereiten.

Diese Grundprinzipien sind das Kernprinzip der Verkapselung und Abstraktion. Das Wissen darüber, wie ein Objekt das tut, was es tut, sollte keine Voraussetzung dafür sein, es zu verwenden, um das zu tun, was es tut. Selbst bei der Computerprogrammierung werden die Details der elektrischen Pfade in der CPU, in der Ihr Programm ausgeführt wird, hinter mindestens einem halben Dutzend Schichten von E / A-Anweisungen, Treibern, Betriebssystemsoftware und Laufzeit abstrahiert. Viele sehr erfolgreiche Softwareentwickler schreiben perfekt guten Code, ohne sich einmal Gedanken über die genaue Hardwarearchitektur oder sogar das Betriebssystem zu machen, mit dem er ausgeführt wird. Mich eingeschlossen.

Das Verbergen von Kapseln / Informationen ermöglicht es der Mentalität, "sich nicht darum zu kümmern, wie es funktioniert, sich nur darum zu kümmern, dass es funktioniert". Ihr Objekt sollte offenlegen, was für den Verbraucher nützlich ist, und zwar auf eine Weise, die der Verbraucher leicht konsumieren kann. Zurück in der realen Welt bedeutet dies nicht, dass ein Auto dem Benutzer keine Informationen über das Innenleben geben sollte, oder dass das Auto dem Benutzer nur die grundlegendsten Funktionen wie Zündung, Lenkrad, und Pedale. Alle Autos haben Tachometer und Kraftstoffanzeigen, Drehzahlmesser, Idiotlichter und anderes Rückgespräch. Praktisch alle Autos haben auch Schalter für verschiedene unabhängige Subsysteme wie Scheinwerfer, Blinker, Radio, Sitzverstellung usw. Einige Autos ermöglichen eine ziemlich esoterische Benutzereingabe, wie die Empfindlichkeit des Sperrdifferentials. In allen Fällen, wenn Sie genug wissen, Sie können es öffnen und ändern, damit es auf etwas andere Weise funktioniert. Aber in den meisten Fällen sollte der Benutzer möglicherweise nicht in der Lage sein, die Kraftstoffpumpen direkt und unabhängig von der Kabine aus zu steuern. Vielleicht, nur vielleicht, sollte der Benutzer nicht in der Lage sein, seine Bremslichter zu aktivieren, ohne das Bremspedal tatsächlich zu betätigen?

Abstraktion erlaubt die "das ist nicht das gleiche wie das, aber weil sie beide X sind, kann ich sie wie ich jede X-Mentalität verwenden." Wenn Ihr Objekt eine Abstraktion erbt oder implementiert, sollten Ihre Konsumenten erwarten, dass Ihre Implementierung dasselbe oder ein ähnliches Ergebnis wie andere bekannte Implementierungen der Abstraktion liefert. Ein Toyota Camry und ein Ford Fusion sind beide "Autos". Als solche verfügen sie über einen gemeinsamen Satz erwarteter Funktionen, beispielsweise ein Lenkrad. Drehen Sie ihn gegen den Uhrzeigersinn, das Auto fährt nach links. Drehen Sie es im Uhrzeigersinn, das Auto fährt nach rechts. Sie können in den USA in jedes Auto einsteigen und erwarten, dass das Auto ein Lenkrad und mindestens zwei Pedale hat, von denen das rechte das "Car Go" -Pedal und das mittlere das "Car Stop" -Pedal ist .

Eine Folge der Abstraktion ist die "Theorie des geringsten Erstaunens". Wenn Sie sich für eine Probefahrt ans Steuer eines neuen Autos setzen, das Lenkrad im Uhrzeigersinn drehen und das Auto nach links drehen, sind Sie, gelinde gesagt, ziemlich erstaunt. Sie würden den Händler beschuldigen, einen POS verkauft zu haben, und würden sich wahrscheinlich keinen seiner Gründe anhören, warum das neue Verhalten "besser" ist als das, was Sie gewohnt sind, oder wie gut dieses Verhalten "dokumentiert" ist oder wie " transparent "ist die Steuerung. Obwohl dieses neue Auto und alle anderen, die Sie gefahren sind, immer noch "Autos" sind, müssen Sie beim Fahren dieses Autos einige grundlegende Konzepte ändern, wie ein Auto gefahren werden soll, um das neue Auto erfolgreich zu fahren. Das ist in der Regel eine schlechte Sache, und dies geschieht nur, wenn das neue Paradigma einen intuitiven Vorteil bietet. Vielleicht ist die Hinzufügung von Sicherheitsgurten ein gutes Beispiel; Vor 50 Jahren stiegen Sie gerade ein und gingen, aber jetzt müssen Sie sich anschnallen. Der intuitive Vorteil besteht darin, dass Sie nicht durch die Windschutzscheibe oder in den Beifahrersitz steigen, wenn Sie einen Unfall haben. Sogar dann widerstanden Fahrer; Viele Autobesitzer schnitten die Sicherheitsgurte aus dem Auto, bis Gesetze verabschiedet wurden, die ihre Verwendung vorschrieben.

KeithS
quelle