Frage

Es ist allgemein anerkannt, dass Kopieren und Einfügen von Programmierung ist eine schlechte Idee, aber was ist der beste Weg, um eine Situation zu handhaben, wo Sie zwei Funktionen oder Code-Blöcke, die wirklich tun müssen, anders sein nur ein paar Möglichkeiten, um sie extrem chaotisch verallgemeinern?

Was passiert, wenn der Code im Wesentlichen das gleiche ist, mit Ausnahme von ein paar kleinen Variationen, aber diese paar kleineren Veränderungen sind nicht in Dinge, die einfach entweder das Hinzufügen eines Parameters, schablonen Methoden ausklammern durch, oder so etwas?

Generell haben Sie jemals eine Situation angetroffen, wo Sie, dass ein wenig copy-and-paste zugeben würde Codierung war wirklich gerechtfertigt ist.

War es hilfreich?

Lösung

Ich habe sagen hören Menschen die kopiert und einmal einfügen (Doppel Code Begrenzung auf maximal zwei Instanzen), als Abstraktionen auszahlen nicht, wenn Sie den Code in drei Stellen oder mehr verwenden. () Ich, ich versuche es eine gute Gewohnheit, Refactoring zu machen, sobald ich die Notwendigkeit zu sehen.

Andere Tipps

Stellen Sie diese Frage über Ihre Funktionen

„wenn diese kleine Anforderung ändert, werde ich beiden Funktionen, um sie ändern muß, um es zu erfüllen?“

Natürlich ist es manchmal akzeptabel. Das ist, warum die Leute Schnipsel Dateien halten. Aber wenn Sie Ausschneiden und Einfügen Code sehr oft, oder mit mehr als ein paar Zeilen, sollten Sie darüber nachdenken, es ist ein Unterprogramm zu machen. Warum? weil die Quoten auf Sie etwas ändern müssen, und auf diese Weise, Sie müssen es nur einmal geändert werden.

Mittlerer Fall ist ein Makro zu verwenden, wenn Sie eine solche zur Verfügung haben.

Ja, und es ist genau so, wie Sie sagen; kleinere, aber schwer zu Faktor Variationen. Sie nicht selbst geißeln, wenn es wirklich das, was die Situation erfordert.

Re ist cut-and-Vergangenheit immer akzeptabel:

Ja. Wenn das Segment etwas anders und Sie tun Einweg-Systeme (Systeme, die es für eine sehr kurze Zeit und nicht gewartet werden müssen). Andernfalls seine in der Regel besser auf die Gemeinsamkeiten zu extrahieren.

Re-Segmente, die ein so aussehen, aber nicht genau gleich:

Wenn die Differenz in den Daten ist, Umgestalten, indem die Funktion, zu extrahieren und die Differenz der Daten als Parameter (Wenn es zu viele sind, um Daten als Parameter zu übergeben, sollten sie in ein Objekt oder eine Struktur Gruppierung). Wenn die Differenz in einem Prozess in der Funktion ist, Refactoring durch Befehl Muster oder abstrakte Vorlage. Wenn es immer noch schwierig ist, selbst mit diesem Design-Patterns, Refactoring, dann könnte Ihre Funktion, die versucht, zu vielen Aufgaben auf seinem eigenen zu behandeln.

Zum Beispiel, wenn Sie ein Codesegment haben, das in zwei Segmenten unterscheidet - diff # 1, und diff # 2. Und in diff # 1 können Sie haben diff1A oder diff1B und für diff # 2 Sie können diff2A und diff2B haben.

Wenn diff1A & diff2A sind immer zusammen, und diff1B & diff2B sind immer zusammen, dann diff1A & diff2A kann in einer Befehlsklasse oder eine abstrakte Vorlage Implementierung und diff1B & diff2B in einem anderen enthalten sein.

Allerdings, wenn es mehrere Kombination (dh diff1A & diff2A, diff1A & diff2B, diff1B & diff2A, diff1B & diff2B), dann mögen Sie vielleicht Ihre Funktion zu überdenken, weil es zu viele Aufgaben zu handhaben kann versuchen, auf eigenem .

Re SQL-Anweisungen:

Mit Logiken (if-else, Schleifen) Ihre SQL dynamisch Lesbarkeit opfert aufzubauen. Aber alle Variationen SQL zu schaffen wäre schwer zu halten. So auf halbem Weg treffen und SQL Segmente verwenden. Extrahieren Gemeinsamkeiten aus als SQL-Segmente und erstellen alle SQL-Varianten mit den SQL-Segmente als Konstanten.

Zum Beispiel:

private static final String EMPLOYEE_COLUMNS = " id, fName, lName, status";

private static final String EMPLOYEE_TABLE = " employee";

private static final String EMPLOYEE_HAS_ACTIVE_STATUS = " employee";

private static final String GET_EMPLOYEE_BY_STATUS =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + EMPLOYEE_HAS_ACTIVE_STATUS;

private static final String GET_EMPLOYEE_BY_SOMETHING_ELSE =
  " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + SOMETHING_ELSE;

In meiner Firma Code-Basis, haben wir eine Reihe von etwa 10 oder so großen haarigen SQL-Anweisungen, die ein hohes Maß an Gemeinsamkeit haben. Alle Aussagen haben einen gemeinsamen Kern, oder zumindest einen Kern, der von einem Wort oder zwei unterscheidet sich nur. Dann könnten Sie die Gruppe 10 Aussagen in 3 oder 4 Gruppierungen, die gemeinsamen Anhängsel an den Kern hinzufügen, wieder mit vielleicht einem oder zwei Worten unterschiedlich in jedem Anhängsel. Jedenfalls denke der 10 SQL-Anweisungen als Sätze in einem Venn-Diagramm mit einer signifikanten Überlappung.

Wir haben uns für diese Aussagen in wie Weise zu codieren, wie Doppelarbeit zu vermeiden. So gibt es eine Funktion (technisch, eine Java-Methode), um die Aussage zu bauen. Es dauert einige Parameter, die für das Wort oder zwei des Unterschiedes in dem gemeinsamen Kern ausmachen. Dann dauert es eine Funktors für das Anhängsel Aufbau aus, was natürlich auch mit mehr Parametern für kleinere Unterschiede und mehr functors für mehr Anhängsel, und so weiter parametriert.

Der Code ist klug, dass keiner der SQL immer wiederholt wird. Wenn Sie jemals eine Klausel in der SQL ändern müssen, ändern Sie es an einem Ort und alle 10 SQL-Anweisungen werden entsprechend geändert.

Aber der Mensch ist der Code schwer zu lesen. Über die einzige Möglichkeit, um herauszufinden, was SQL für einen bestimmten Fall ausgeführt werden wird ist mit einem Debugger zu Schritt durch und die SQL auszudrucken, nachdem es vollständig zusammengebaut worden ist. Und herauszufinden, wie eine bestimmte Funktion, die eine Klausel Bild paßt in die größeren erzeugt, ist böse.

Da dieses Schreiben habe ich oft gefragt, ob wir besser gewesen wären dran, nur Schneiden-und-Einfügen der SQL-Abfrage 10 mal. Natürlich, wenn wir dies tun, um die SQL jede Änderung muß in 10 Orten auftreten können, aber Kommentare uns auf die 10 Plätze zeigen könnten helfen, aktualisieren.

Der Vorteil, dass die SQL verständlich und alles an einem Ort würde überwiegen wahrscheinlich die Nachteile des Schneidens-und-Einfügen der SQL.

Wie Martin Fowler schon sagt,

tut es einmal, in Ordnung.

tut es zweimal, beginnt zu riechen.

tut es dreimal, um Zeit zu refactor .


EDIT: in Antwort auf den Kommentar, ist der Ursprung der Beratung Don Robert:

Drei Streiks und Sie Refactoring .

Martin Fowler beschreibt, dass in Refactoring Kapitel 2, Abschnitt des Dreisatzes (Seite 58).

ABSOLUT NEEEVER ..

:)

Sie können den Code in Frage posten und sehen, dass es einfacher ist, als das, was es sieht aus wie

Wenn es der einzige Weg ist, es zu tun, dann gehen sie. Oft (je nach Sprache) können Sie kleinere Änderungen an der gleichen Funktion mit einem optionalen Argument erfüllen.

Vor kurzem hatte ich eine add () Funktion und bearbeitet () Funktion in einem PHP-Skript. Beide haben praktisch die gleiche Sache, aber die Bearbeitung () Funktion ausgeführt, um eine UPDATE-Abfrage statt eine INSERT-Abfrage. Ich habe gerade so etwas wie

function add($title, $content, $edit = false)
{
    # ...
    $sql = edit ? "UPDATE ..." : "INSERT ...";
    mysql_unbuffered_query($sql);
}

hat sehr gut funktioniert - aber es gibt auch andere Zeiten, in denen Copy / Paste notwendig. Verwenden Sie nicht etwas seltsam, komplizierten Weg, es zu verhindern.

  1. Guter Code ist wieder verwendbarer Code.
  2. Sie das Rad nicht neu erfinden.
  3. existieren Beispiele für einen Grund: zu helfen, zu lernen und im Idealfall Code besser
  4. .

Wenn Sie kopieren und einfügen? Wen interessiert das! Was wichtig ist, ist Warum Sie sind Kopie und Einfügen. Ich versuche nicht, hier philosophisch auf jemand zu bekommen, aber lassen Sie uns darüber nachdenken praktisch:

Ist es aus Faulheit? „Blah blah, ich habe das schon mal gemacht ... Ich ändere nur einige Variablennamen .. getan.“

Kein Problem, wenn es bereits guter Code, bevor Sie es kopiert und eingefügt. Ansonsten sind Sie aus Faulheit crappy Code zu verewigen, die Ihren Esel auf der Straße beißen wird.

Ist es, weil Sie nicht verstehen? „Verdammt .. Ich verstehe nicht, wie diese Funktion funktioniert, aber ich frage mich, ob es in meinem Code arbeiten werde ..“ Es könnte! Dies können Ihnen Zeit im unmittelbaren Moment sparen, wenn Sie gestresst ist, dass Sie einen Termin bei 09.00 Uhr und Sie um 04.00 Uhr an einer roten Augen starren sind

Wollen Sie diesen Code verstehen, wenn Sie zu ihm zurückkehren? Auch wenn Sie es kommentieren? Nein wirklich - nach Tausenden von Codezeilen, wenn Sie nicht verstehen, was der Code tut, wie Sie es schreiben, wie werden Sie verstehen kommenden Wochen, um es zurück, Monate später? Der Versuch, es zu lernen, trotz aller Versuchung anders. Geben Sie es aus, wird dies dazu beitragen, es einprägen. Jede Zeile, die Sie geben, fragen Sie sich, was diese Zeile tut und wie es trägt zu dem Gesamtzweck dieser Funktion. Auch wenn Sie es nicht von innen nach außen lernen, könnten Sie eine Chance haben, es zumindest zu erkennen, wenn Sie es später wieder auf.

So - das Kopieren und Einfügen von Code? Fein, wenn Sie bewusst die Bedeutung dessen, was Sie tun. Andernfalls? Tun Sie es nicht. Also, stellen Sie sicher, dass Sie eine Kopie der Lizenz eines 3rd-Party-Code haben Sie kopieren und einfügen. Scheint die gesunden Menschenverstand, aber Sie würden, wie viele Leute überrascht sein, dies nicht tun.

Ich vermeide Ausschneiden und Einfügen wie die Pest. Es ist sogar noch schlimmer als sein Cousin Klon und ändern. Wenn mit einer solchen Situation konfrontiert Ihnen bin ich immer bereit, ein Makroprozessor oder ein anderes Skript verwenden, um die verschiedenen Varianten zu erzeugen. Nach meiner Erfahrung eines Single Point of Truth ist enorm wichtig.

Leider ist der C-Makroprozessor nicht sehr gut für diesen Zweck, da die lästigen Stellungsanforderungen für neue Zeilen, Ausdrücke, Anweisungen und Argumente. Ich hasse es zu schreiben

#define RETPOS(E) do { if ((E) > 0) then return; } while(0)

aber das Zitat ist eine Notwendigkeit. Ich werde oft den C-Präprozessor trotz ihrer Mängel verwenden, da es nicht ein weiteres Element zur Werkzeugkette hinzufügt und so erfordert nicht den Build-Prozess oder Makefiles zu ändern.

Ich bin dieses froh ist markiert als subjektiv, da es sicher ist! Dies ist eine zu vage Beispiel, aber ich könnte mir vorstellen, dass, wenn Sie genug Code haben, dupliziert, dass Sie diese Abschnitte abstrakt könnte und halten die verschiedenen Teile unterschiedlich. Der Punkt, der nicht kopier Einfügens ist so am Ende Sie keinen Code haben, die schwer zu pflegen und zerbrechlich ist.

Der beste Weg (neben umwandeln in gängigen Funktionen oder die Verwendung von Makros) ist Kommentare zu setzen. Wenn Sie einen Kommentar auf das der Code kopiert von und nach, und was die Gemeinsamkeit ist, und die Unterschiede, und der Grund, es zu tun ... dann werden Sie in Ordnung sein.

Wenn Sie feststellen, dass Sie Funktionen haben, die meist gleich sind, aber in verschiedenen Szenarien leichte Verbesserungen erfordern, ist es Ihr Design, das ist das Problem. Verwenden Sie Polymorphismus und Zusammensetzung anstelle von Fahnen oder Copy-Paste.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top