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?
time-management
problem-solving
abstraction
Bryan Harrington
quelle
quelle
Antworten:
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.
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.
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.
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.
quelle
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.
quelle
simple
undeasy
!Ein Syllogismus:
Allgemeinheit ist teuer.
Sie geben das Geld anderer Leute aus.
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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.
quelle
Ich bin nicht einverstanden mit "lösen Sie es dumm" , ich denke, es kann mehr "lösen Sie es klug" sein .
Was ist schlauer:
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.
quelle