Vor kurzem hatten wir in meiner Firma eine Debatte über Abstraktion vs. Einfachheit. Eine Denkrichtung, die ich als "TROCKEN und Abstraktion kann keinen Schaden anrichten" bezeichnen würde, führt zu Code wie diesem:
def make_foo_binary(binaryName, objFiles, fooLibsToLinkAgainst)
make_exe_task(binaryName, objFiles.ext('.o'), fooLibsToLinkAgainst)
end
und das:
class String
def escape_space
return self.gsub(' ', '\ ')
end
end
Mein Standpunkt ist, dass das Erstellen einer solchen Abstraktion, die nur an einer Stelle verwendet wird, den Code weniger lesbar macht, da Sie einen Funktionsaufruf, mit dem der Leser vertraut ist (gsub), durch einen anderen ersetzen, den er noch nie gesehen hat vor (Escape_Space), die sie lesen müssen, um zu verstehen, wie der Code tatsächlich funktioniert. Der Ersatz wird im Wesentlichen in Englisch ("Fluchtraum") beschrieben und Englisch ist notorisch vage. Ohne die Definition zu betrachten, wissen Sie beispielsweise nicht, ob sie alle Leerzeichen oder nur das Leerzeichen enthält.
Es gibt viel Schreiben, das das Lob von TROCKEN und Abstraktion singt. Kennt jemand Quellen, die die Grenzen der Abstraktion beschreiben? Das Lob singen und die Pragmatik diskutieren, Code einfach zu halten?
Bearbeiten: Ich kann Texte finden, die die Einfachheit im Leben oder beim (englischen) Schreiben fördern, z. B. Thoreaus "Simplify, Simplify!" oder Strunk and Whites "Kräftiges Schreiben ist prägnant." Wo ist das Äquivalent für die Programmierung?
quelle
Antworten:
Sicher: Joel Spolsky (Sie haben vielleicht von ihm gehört) sagt :
Siehe auch KISS und YAGNI, die Jeff Atwood bespricht :
(Das genaue Angebot finden Sie unter dem Link.)
Meine Interpretation dieser Zitate:
Es ist wirklich schwer, gute Abstraktionen zu erstellen - es ist viel einfacher, beschissene zu erstellen.
Wenn Sie eine Abstraktion hinzufügen, für die keine Notwendigkeit besteht oder die falsch ist, verfügen Sie jetzt über zusätzlichen Code, der getestet, debuggt und gewartet werden muss. Und wenn Sie zurückgehen und umgestalten müssen, haben Sie jetzt mehr Eigengewicht, das Sie nach unten zieht.
quelle
Ich bin mit der Sprache oder Laufzeit Ihrer Beispiele nicht vertraut, habe aber für
gsub
mich keine Bedeutung. Istescape_space
jedoch viel aussagekräftiger. Ich bin völlig anderer Meinung als Ihr Argument, dass bekannte Funktionsaufrufe nicht durch Aufrufe von Funktionen ersetzt werden sollten, die sie noch nie zuvor gesehen haben . Wenn ich dieses Argument auf die Spitze treiben würde, sollte man einem Programm niemals eine neue Funktion hinzufügen: Es abstrahiert immer bekannte Funktionsaufrufe und ersetzt sie immer durch diesen neuen unbekannten benutzerdefinierten Aufruf.Ein Entwickler sollte niemals den Code lesen müssen, um zu verstehen, wie er funktioniert. Zuallererst sollte er oder sie sich nicht darum kümmern müssen, wie es funktioniert, sondern sollte verstehen können, wie man es benutzt und welche Auswirkungen es hat. Ist dies nicht der Fall, liegt ein Problem mit dem Namen und der Signatur der Funktion oder ihrer Dokumentation vor.
Für mich ist das Ziel der Abstraktion, innere Details zu entfernen und eine sauberere Schnittstelle zu einigen Funktionen bereitzustellen. In Zukunft könnte das Innenleben der
escape_space
Funktion geändert oder überschrieben werden. Es ist mir egal, dass eine Funktiongsub
mit zwei Argumenten aufgerufen wird. Es ist mir wichtig, dass meine Räume entkommen. Das macht die Abstraktion also nützlich.In meiner Lieblingssprache C # füge ich allen meinen Funktionen (auch privaten) immer eine Dokumentation hinzu, in der Dinge wie Funktion, Verwendung, verwendete Einheiten, ausgelöste Ausnahmen, Eingabevertrag und Typen beschrieben werden. Mit IntelliSense von Visual Studio kann jeder Entwickler klarer sehen, was die Funktion tut, ohne den Code zu lesen . Ohne ein Tool wie IntelliSense müssen Entwickler ihre Funktionsnamen genauer festlegen oder zusätzliche Dokumentationen an einem leicht zugänglichen Ort aufbewahren.
Ich denke nicht, dass Abstraktionen jemals eingeschränkt werden sollten, und ich kenne keine solche Quelle.
quelle
sed
und vim:s
) ist 'g' verwendet als "alle"gsub
,atoi
undwcstombs
als viel schlimmer genannt werdenescape_space
."something".gsub(' ', '\ ')
, wissen Sie genau, welche Ausgabe Sie erhalten. Das Entkommen von Räumen sehe ich nicht sehr oft. Verwendet es das Zeichen "\" oder ein anderes Escape-Zeichen? Entgeht es Zeilenumbrüchen? Ganz zu schweigen davon, dass es sich um ein Monkeypatch der Standard-String-Klasse handelt, das von bestimmten Entwicklern als schlechte Praxis angesehen wird.Wenn ich über Abstraktionen spreche, denke ich, dass es wichtig ist, zwei Begriffe zu definieren: Inhärente und zufällige Komplexität. Inhärente Komplexität ist die Komplexität des Problems, das die Abstraktion löst. Zufällige Komplexität ist die Komplexität, die die Abstraktion verbirgt.
Gute Abstraktionen sind solche, die viel zufällige Komplexität verbergen und versuchen, die richtigen Probleme zu lösen, indem sie ihre inhärente Komplexität verringern. Ich denke, was Sie hier frustriert, sind Abstraktionen, die zu flach sind; Sie verbergen nicht viel zufällige Komplexität und es ist genauso schwierig (wenn nicht noch schwieriger), die Abstraktionen zu verstehen, wie es ist, ihre Implementierungen zu verstehen.
quelle
Meine Antwort wird mehr von der persönlichen Erfahrung abweichen, aber eine schnelle Suche hat diesen Blog gefunden , der ziemlich detailliert darüber aussieht, wann die Abstraktion zu weit geht (nicht nur der verknüpfte Eintrag, sondern es gibt mehrere verwandte).
Grundsätzlich kann Abstraktion eine gute Sache sein. Wie alles andere kann man sich jedoch mitreißen lassen. Ihre Beispiele sind gute, bei denen die Abstraktion zu weit gegangen ist. Sie erstellen "Pass-Throughs", die im Grunde nichts anderes tun, als die betreffende Funktion umzubenennen.
Bei benutzerdefinierten Funktionen kann dies erforderlich sein, wenn Sie ein altes Namensschema zugunsten eines neuen verwerfen möchten (z. B. das Ändern von Escape_space in EscapeSpace oder EscWhitespace oder was auch immer). Für Bibliotheksfunktionen? Overkill. Ganz zu schweigen von kontraproduktiv. Was haben Sie durch diese Abstraktion gewonnen? Wenn das alles ist es tut, dann alles haben Sie gewonnen ist die Kopfschmerzen zu haben , die Funktion zu erinnern und dieses Wissen an jemand anderen übergeben (während mit Bibliotheksfunktionen, Entwickler sind entweder bereits Kenntnis von ihnen, oder sollte eingeben können ,
ruby gsub
in Google und finden Sie heraus, was es bedeutet).Ich empfehle, intelligenter zu bestimmen, wann etwas abstrahiert werden soll. Beispielsweise sind Dinge, die mehr als 2-3 Zeilen umfassen und mindestens zweimal verwendet werden (selbst wenn es geringfügige Unterschiede gibt, können diese Unterschiede häufig parametrisiert werden), im Allgemeinen gute Abstraktionskandidaten. Manchmal betrachte ich große Codeblöcke, die aus Gründen der Lesbarkeit und des "One Job" -Prinzips eine bestimmte Funktion innerhalb einer größeren Funktion ausführen, und abstrahiere sie in geschützte Funktionen innerhalb derselben Klasse (häufig unmittelbar) Nähe der Funktion, die es verwendet).
Vergessen Sie auch nicht YAGNI (Sie werden es nicht brauchen) und vorzeitige Optimierung. Wird dies derzeit mehrmals verwendet? Nein? Dann machen Sie sich jetzt keine Sorgen darüber, es zu abstrahieren (es sei denn, dies führt zu einem erheblichen Lesbarkeitsgewinn). "Aber wir könnten es später brauchen!" Dann abstrahieren Sie es, wenn Sie es tatsächlich woanders brauchen. Bis dahin haben Sie nur Zeiger auf Zeiger auf Zeiger ohne wirklichen Nutzen.
Auf der anderen Seite dieser Medaille, wenn Sie etwas abstrahieren, tun Sie dies, sobald die Notwendigkeit offensichtlich wird. Es ist einfacher, zwei oder drei Codeblöcke zu verschieben und zu optimieren als 15. Im Allgemeinen denke ich, sobald ich Codeblöcke von einem anderen Ort kopiere oder etwas schreibe, das mir sehr vertraut erscheint, darüber nach, wie ich es abstrahieren kann.
quelle
Schauen Sie sich zunächst dieses Beispiel von Robert C. Martin an:
http://blog.objectmentor.com/articles/2009/09/11/one-thing-extract-till-you-drop
Der Grund für diese Art des Refactorings besteht darin, Abstraktionen zu erstellen, um sehr kleine Funktionen zu erstellen, sodass jede Funktion nur eines tut . Das führt zu Funktionen wie den, die Sie uns oben gezeigt haben. Ich halte dies für eine gute Sache - es hilft Ihnen, Code zu erstellen, bei dem sich jede aufgerufene Funktion innerhalb einer anderen Funktion auf derselben Abstraktionsebene befindet.
Wenn Sie der Meinung sind, dass Sie Namen wie verwenden
make_foo_binary
oderescape_space
den Code weniger lesbar machen, sollten Sie versuchen, bessere Namen zu wählen (zum Beispielescape_space_chars_with_backslash
:), anstatt die Abstraktion aufzugeben.quelle