Abstraktion: Der Krieg zwischen Problemlösung und allgemeiner Lösung

33

Als Programmierer befinde ich mich in einem Dilemma, in dem ich mein Programm so abstrakt und allgemein wie möglich gestalten möchte.

Wenn ich dies normalerweise tue, kann ich meinen Code wiederverwenden und eine allgemeinere Lösung für ein Problem finden, das möglicherweise erneut auftritt (oder auch nicht).

Dann sagt diese Stimme in meinem Kopf: Löse einfach das Problem, Dummy ist so einfach! Warum mehr Zeit verbringen als nötig?

Wir alle haben uns in der Tat dieser Frage gestellt, bei der sich Abstraktion auf Ihrer rechten Schulter befindet und Solve-it-dumm auf der linken.

Was soll ich hören und wie oft? Was ist Ihre Strategie dafür? Solltest du alles abstrahieren?

Bryan Harrington
quelle
1
"So abstrakt und so allgemein" ist allgemein bekannt als die Hybris des Programmierers :) Aufgrund des Murphy-Gesetzes müssen Sie jedoch nur einen Grad an Anpassung vornehmen, um an der nächsten Aufgabe zu arbeiten, den Sie nicht angegeben haben.
Matthieu M.
1
Eine der besten Fragen aller Zeiten!
Explorest
1
Sie raten immer falsch, also versuchen Sie es einfach. Wenn Sie die einfachen Fälle "genug Male" erweitert haben, sehen Sie ein Muster. Bis dahin nicht.

Antworten:

27

Was soll ich hören und wie oft?

Nie abstrakt, bis Sie müssen.

In Java müssen Sie beispielsweise Schnittstellen verwenden. Sie sind eine Abstraktion.

In Python gibt es keine Schnittstellen, Duck Typing und keine überragenden Abstraktionsebenen. Also tust du es einfach nicht.

Was ist Ihre Strategie dafür?

Abstrakt erst, wenn du es dreimal geschrieben hast.

Einmal ist - na ja - einmal. Einfach lösen und weitermachen.

Zweimal deutet dies darauf hin, dass hier möglicherweise ein Muster vorliegt. Oder kann es nicht. Es kann nur Zufall sein.

Dreimal ist der Beginn eines Musters. Jetzt geht es über den Zufall hinaus. Sie können jetzt erfolgreich abstrahieren.

Solltest du alles abstrahieren?

Nein.

In der Tat sollten Sie niemals etwas abstrahieren, bis Sie den absoluten Beweis dafür haben, dass Sie die richtige Art von Abstraktion tun. Ohne die "Regel der drei Wiederholungen" werden Sie Dinge schreiben, die nutzlos auf eine Art und Weise abstrahiert sind, die nicht hilfreich ist.

Wenn ich das normalerweise mache, kann ich meinen Code wiederverwenden

Diese Annahme ist oft falsch. Abstraktion kann überhaupt nicht helfen. Es kann schlecht gemacht werden. Tun Sie es daher erst, wenn Sie es müssen.

S.Lott
quelle
1
"Niemals abstrakt, bis du musst." - Ich nehme an, Sie vermeiden die Verwendung von Klassen, Methoden, Schleifen oder if-Anweisungen? Diese Dinge sind alles Abstraktionen. Wenn Sie darüber nachdenken, ist Code, der in Hochsprachen geschrieben wurde, sehr abstrakt, egal ob Sie ihn mögen oder nicht. Die Frage ist nicht, ob man abstrahieren soll. Es ist, welche Abstraktionen zu verwenden sind.
Jason Baker
9
@ Jason Baker: Ich nehme an, Sie vermeiden die Verwendung von Klassen, Methoden, Schleifen oder if-Anweisungen. Das scheint nicht die Frage zu sein, um die es ging. Warum die absurde Behauptung aufstellen, dass jegliche Programmierung unmöglich ist, wenn wir vermeiden, unsere Entwürfe zu stark zu abstrahieren?
S.Lott 31.12.10
1
Ich erinnere mich an den alten Witz, bei dem ein Mann eine Frau fragt, ob sie für eine Million Dollar mit ihm schlafen werde. Wenn sie ja sagt, fragt er, ob sie für fünf Dollar mit ihm schlafen werde. Wenn sie sagt "Für was für eine Frau halten Sie mich?" Die Antwort lautet: "Nun, wir haben bereits festgestellt, dass Sie mit jemandem schlafen werden, um Sex zu haben. Jetzt feilschen wir nur noch um den Preis." Mein Punkt ist, dass es nie um eine Entscheidung geht, zu abstrahieren oder nicht. Es geht darum, wie viel zu abstrahieren und welche Abstraktionen zu wählen sind. Sie können sich nicht dafür entscheiden, die Abstraktion aufzuhalten, wenn Sie nicht reine Assemblierung schreiben.
Jason Baker
9
@Jason Baker: "Sie können sich nicht entscheiden, die Abstraktion zu unterbrechen, wenn Sie nicht reine Assemblierung schreiben." Das ist trivial falsch. Assembly ist eine Abstraktion über Maschinensprache. Welches ist eine Abstraktion über die Hardware-Schaltung. Bitte hören Sie auf, zu viel in die Antwort zu lesen, die überhaupt nicht vorhanden war. Die Frage ging nicht um "Abstraktion" als Konzept oder intellektuelles Werkzeug. Es ging um Abstraktion als OO-Designtechnik, die zu oft falsch angewendet wurde. Meine Antwort war nicht, dass Abstraktion unmöglich ist. Aber diese "Überabstraktion" ist schlecht.
S.Lott
1
@JasonBaker es ist kein unerhörter Grund mit jemandem zu schlafen. Vielleicht haben Sie an "Geld" gedacht?
19

Ah, YAGNI. Das am meisten missbrauchte Programmierkonzept.

Es gibt einen Unterschied, ob Sie Ihren Code generisch machen oder zusätzliche Arbeit leisten. Sollten Sie zusätzliche Zeit aufwenden, um Ihren Code locker zu koppeln und einfach an andere Aufgaben anzupassen? Absolut. Sollten Sie Zeit damit verbringen, nicht benötigte Funktionen zu implementieren? Nein. Sollten Sie Zeit damit verbringen, Ihren Code mit anderem Code zu arbeiten, der noch nicht implementiert wurde? Nein.

Wie das Sprichwort sagt "Tun Sie das Einfachste, was möglicherweise funktionieren könnte". Die Sache ist, dass die Leute immer einfach mit leicht verwechseln . Einfachheit braucht Arbeit. Aber die Mühe lohnt sich.

Kannst du über Bord gehen? Na sicher. Ich habe jedoch die Erfahrung gemacht, dass nur wenige Unternehmen eine stärkere Einstellung zum "Jetzt erledigen" benötigen, als sie dies bereits getan haben. Die meisten Unternehmen brauchen mehr Leute, die die Dinge im Voraus durchdenken. Andernfalls haben sie immer ein sich selbst tragendes Problem, bei dem niemand Zeit für irgendetwas hat, jeder immer in Eile ist und niemand etwas erledigt.

Stellen Sie sich das so vor: Die Chancen stehen gut, dass Ihr Code irgendwie wieder verwendet wird. Aber Sie werden das nicht richtig vorhersagen. So machen Sie Ihren Code sauber, abstrakt und lose gekoppelt. Versuchen Sie jedoch nicht, Ihren Code mit bestimmten Funktionen arbeiten zu lassen, die noch nicht vorhanden sind. Auch wenn Sie wissen, dass es in Zukunft existieren wird.

Jason Baker
quelle
Schöne Unterscheidung zwischen simpleund easy!
Matthieu M.
"Aber ich habe die Erfahrung gemacht, dass es nur wenige Unternehmen gibt" - interessanterweise mag das Gegenteil im akademischen Bereich der Fall sein.
Steve Bennett
12

Ein Syllogismus:

  1. Allgemeinheit ist teuer.

  2. Sie geben das Geld anderer Leute aus.

  3. Daher muss der Aufwand der Allgemeinheit gegenüber den Stakeholdern gerechtfertigt sein.

Fragen Sie sich, ob Sie das allgemeinere Problem wirklich lösen, um den Beteiligten später Geld zu sparen, oder ob Sie es nur als intellektuelle Herausforderung empfinden, eine unnötig allgemeine Lösung zu finden.

Wenn Allgemeingültigkeit wünschenswert ist , sollte sie wie jedes andere Merkmal entworfen und getestet werden . Wenn Sie keine Tests schreiben können, die belegen, wie die von Ihnen implementierte Allgemeingültigkeit ein in der Spezifikation gefordertes Problem löst, müssen Sie das nicht tun! Eine Funktion, die keine Designkriterien erfüllt und nicht getestet werden kann, ist eine Funktion, auf die sich niemand verlassen kann.

Und schließlich gibt es kein "so allgemein wie möglich". Angenommen, Sie schreiben Software in C #, nur aus Gründen der Argumentation. Wird jede Klasse eine Schnittstelle implementieren? Jede Klasse eine abstrakte Basisklasse mit jeder Methode eine abstrakte Methode? Das ist ziemlich allgemein, aber bei weitem nicht "so allgemein wie möglich". Dies ermöglicht es den Benutzern lediglich, die Implementierung einer Methode durch Unterklassen zu ändern. Was ist, wenn sie die Implementierung einer Methode ohne Unterklassen ändern möchten? Sie können jede Methode tatsächlich zu einer Eigenschaft des Delegattyps mit einem Setter machen, sodass die Benutzer jede Methode in etwas anderes ändern können. Aber was ist, wenn jemand mehr Methoden hinzufügen möchte? Jetzt muss jedes Objekt erweiterbar sein.

An diesem Punkt sollten Sie C # verlassen und JavaScript einbeziehen. Aber Sie sind immer noch nicht allgemein genug angekommen. Was ist, wenn jemand ändern möchte, wie die Mitgliedersuche für dieses bestimmte Objekt funktioniert? Vielleicht sollten Sie stattdessen alles in Python schreiben.

Zunehmende Allgemeinheit bedeutet oft, die Vorhersehbarkeit aufzugeben und die Testkosten massiv zu erhöhen, um sicherzustellen, dass die implementierte Allgemeinheit tatsächlich einen tatsächlichen Benutzerbedarf erfüllt . Sind diese Kosten durch ihren Nutzen für die Stakeholder gerechtfertigt, die dafür zahlen? Vielleicht sind sie es; Sie können dies mit den Stakeholdern besprechen. Meine Stakeholder sind keineswegs bereit, die statische Eingabe aufzugeben, um ein völlig unnötiges und extrem teures Maß an Allgemeingültigkeit zu erreichen, aber vielleicht sind es Ihre.

Eric Lippert
quelle
2
+1 für nützliche Prinzipien, -1 für Geldverdienen.
Mason Wheeler
4
@Mason: Es geht nicht um Geld , es geht um Mühe . Geld ist ein praktisches Maß an Anstrengung . Effizienz ist der Nutzen, der sich aus dem erzielten Aufwand ergibt. Wiederum ist der Geldgewinn ein praktisches Maß für die aufgelaufenen Vorteile . Es ist einfach einfacher, die Abstraktion von Geld zu diskutieren, als einen Weg zu finden, um Aufwand, Kosten und Nutzen zu messen. Möchten Sie Aufwand, Kosten und Nutzen lieber auf andere Weise messen? Zögern Sie nicht, einen Vorschlag für einen besseren Weg zu machen.
Eric Lippert
2
Ich stimme dem Punkt von @Mason Wheeler zu. Ich finde, dass die Lösung darin besteht, keine Stakeholder in diese Ebene eines Projekts einzubeziehen. Dies hängt natürlich stark von der Branche ab, aber Kunden sehen den Code normalerweise nicht. Auch scheinen alle diese Antworten Anti-Mühe zu sein, scheint faul zu sein.
Orbling
2
@Orbling: Ich bin entschieden gegen unnötige, teure und verschwenderische Anstrengungen, die wertvolle und wichtige Projekte ressourcenschonend machen. Wenn Sie vorschlagen, dass dies mich "faul" macht, dann gebe ich an, dass Sie das Wort "faul" auf ungewöhnliche Weise verwenden.
Eric Lippert
1
Nach meiner Erfahrung verschwinden andere Projekte nicht so schnell, dass es sich in der Regel lohnt, so viel Zeit wie nötig in das aktuelle Projekt zu investieren, bevor Sie fortfahren.
Orbling
5

Nur um klar zu sein, etwas zu verallgemeinern und eine Abstraktion zu implementieren, sind zwei völlig verschiedene Dinge.

Stellen Sie sich zum Beispiel eine Funktion vor, die den Speicher kopiert.

Die Funktion ist eine Abstraktion, die verbirgt, wie die 4 Bytes kopiert werden.

int copy4Bytes (char * pSrc, char * pDest)

Eine Verallgemeinerung würde bewirken, dass diese Funktion eine beliebige Anzahl von Bytes kopiert.

int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)

Die Abstraktion kann wiederverwendet werden, während die Verallgemeinerung die Abstraktion in mehr Fällen nur nützlich macht.

Im Zusammenhang mit Ihrer Frage ist Abstraktion nicht nur aus Sicht der Wiederverwendung von Code nützlich. Wenn es richtig gemacht wird, wird Ihr Code oft lesbarer und wartbarer. Was ist anhand des obigen Beispiels einfacher zu lesen und zu verstehen, wenn Sie den Code copyBytes () oder eine for-Schleife durchlaufen, um Daten indexweise zu verschieben? Abstraktion kann eine Art Selbstdokumentation liefern, die meiner Meinung nach die Arbeit mit dem Code erleichtert.

Als meine eigene Faustregel gilt: Wenn ich einen guten Funktionsnamen finden kann, der genau beschreibt, was ich mit einem Code vorhabe, schreibe ich eine Funktion dafür, unabhängig davon, ob ich denke, dass ich ihn wieder verwenden werde oder nicht.

Pemdas
quelle
+1 für den Unterschied zwischen Abstraktion und Generalisierung.
Joris Meys
4

Eine gute allgemeine Regel für solche Dinge ist Zero, One, Infinity. Das heißt, wenn Sie das, was Sie schreiben, mehrmals benötigen, können Sie davon ausgehen, dass Sie es noch öfter benötigen und verallgemeinern. Diese Regel impliziert, dass Sie sich nicht mit Abstraktion beschäftigen, wenn Sie zum ersten Mal etwas schreiben.

Ein weiterer guter Grund für diese Regel ist, dass Sie beim ersten Schreiben des Codes nicht unbedingt wissen, was zu abstrahieren ist, da Sie nur ein Beispiel haben. Murphys Gesetz impliziert, dass, wenn Sie das erste Mal abstrakten Code schreiben, das zweite Beispiel Unterschiede aufweist, die Sie nicht erwartet haben.

Larry Coleman
quelle
1
Das klingt sehr nach meiner Version der Refactoring-Regel: Strike Two! Refactor!
Frank Shearar
@Frank: Es ist wahrscheinlich Ihre Refactoring-Regel, aber der zweite Absatz wurde aus eigener Erfahrung hinzugefügt.
Larry Coleman
+1: Ich glaube, dass dies auch in The Pragmatic Programmer ziemlich früh bemerkt wird, fast wörtlich IIRC.
Steven Evers
auf jeden Fall. Mit nur einem Beispiel gibt es nichts zu abstrahieren: Sobald Sie ein zweites Beispiel haben, können Sie sehen, was gemeinsam ist (gemeinsam genutzt wird) und was nicht, und Sie können weg abstrahieren!
Frank Shearar
Eine Stichprobe von zwei ist meiner Meinung nach zu klein, um sie auf unendlich zu verallgemeinern. Mit anderen Worten, viel zu früh.
2

Eric Lippert weist auf drei Dinge hin, die meines Erachtens in seinem Artikel zur Zukunftssicherung eines Designs zutreffen . Ich denke, wenn Sie es befolgen, werden Sie in guter Verfassung sein.

Erstens: Vorzeitige Allgemeinheit ist teuer.

Zweitens: Stellen Sie in Ihrem Modell nur die Dinge dar, die sich immer in der Problemdomäne befinden und deren Klassenbeziehungen sich nicht ändern.

Drittens: Halten Sie Ihre Richtlinien von Ihren Mechanismen fern.

Conrad Frix
quelle
1

Es hängt davon ab, warum Sie codieren, der Zweck Ihres Projekts. Wenn der Wert Ihres Codes darin besteht, ein konkretes Problem zu lösen, möchten Sie dies erledigen und mit dem nächsten Problem fortfahren. Wenn es schnelle und einfache Dinge gibt, die Sie tun können, um zukünftigen Benutzern des Codes (einschließlich Ihnen selbst) die Arbeit zu erleichtern, sollten Sie auf jeden Fall angemessene Vorkehrungen treffen.

Andererseits gibt es Fälle, in denen der von Ihnen geschriebene Code einem allgemeineren Zweck dient. Zum Beispiel, wenn Sie eine Bibliothek für andere Programmierer schreiben, die sie in einer Vielzahl von Anwendungen verwenden. Die potenziellen Benutzer sind unbekannt und Sie können sie nicht genau fragen, was sie wollen. Aber Sie möchten Ihre Bibliothek allgemein nützlich machen. In solchen Fällen würde ich mehr Zeit darauf verwenden, die allgemeine Lösung zu unterstützen.

Rob Weir
quelle
0

Ich bin ein großer Fan des KISS-Prinzips.

Ich konzentriere mich darauf, das zu liefern, worum ich gebeten werde, und nicht darauf, was die beste Lösung ist. Ich musste den Perfektionismus (und die Zwangsstörung) loslassen, weil es mich elend machte.

Pablo
quelle
Warum die Abneigung gegen Perfektionismus? (Ich habe Sie übrigens nicht
abgewählt
Das Streben nach Perfektion funktioniert bei mir nicht. Warum? Weil ich weiß, dass meine Programme niemals perfekt sein werden. Es gibt keine Standarddefinition für Perfektion, deshalb entscheide ich mich einfach dafür, etwas bereitzustellen, das gut funktioniert.
Pablo
-1

wo Abstraktion auf deiner rechten Schulter ist und Solve-it-dumm auf der linken sitzt.

Ich bin nicht einverstanden mit "lösen Sie es dumm" , ich denke, es kann mehr "lösen Sie es klug" sein .

Was ist schlauer:

  • Schreiben einer komplexen verallgemeinerten Lösung, die mehrere Fälle unterstützen könnte
  • Schreiben kurz und effecient Code, löst das Problem auf der Hand und ist leicht zu pflegen, und das kann sein erweitert in Zukunft IF es erforderlich ist.

Die Standardeinstellung sollte die zweite sein. Es sei denn, Sie können einen Generationsbedarf nachweisen.

Die verallgemeinerte Lösung sollte nur dann sein, wenn Sie wissen, dass sie in mehreren verschiedenen Projekten / Fällen verwendet wird.

Normalerweise finde ich, dass verallgemeinerte Lösungen am besten als "Kernbibliothekscode" bereitgestellt werden.

Dunkle Nacht
quelle
Wenn Sie hier alle Annahmen treffen könnten, hätten Sie kein Problem. Kurz und pflegeleicht schließen sich gegenseitig aus. Wenn Sie wissen, dass etwas wiederverwendet wird, wird dies mit der allgemeinen Lösung behoben.
JeffO
@ Jeff Ich bin nicht ganz sicher, ob ich Ihren Kommentar verstehe ..
Darknight
Sie haben "Solve it Stupid" aus dem Kontext genommen.
Bryan Harrington