Ist es akzeptabel, langen, aber unkomplizierten Code zu kopieren und einzufügen, anstatt ihn in eine Klasse oder Funktion zu packen?

29

Angenommen, ich habe ein Codesegment, um eine Verbindung zum Internet herzustellen und die entsprechenden Verbindungsergebnisse anzuzeigen:

HttpRequest* httpRequest=new HttpRequest();
httpRequest->setUrl("(some domain .com)");
httpRequest->setRequestType(HttpRequest::Type::POST);
httpRequest->setRequestData("(something like name=?&age=30&...)");
httpRequest->setResponseCallback([=](HttpClient* client, HttpResponse* response){
    string responseString=response->getResponseDataString();
        if(response->getErrorCode()!=200){
            if(response->getErrorCode()==404){
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setFontColor(255,255,255);
                alert->setPosition(Screen.MIDDLE);
                alert->show("Connection Error","Not Found");
            }else if((some other different cases)){
                (some other alert)
            }else
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setPosition(Screen.MIDDLE);
                alert->setFontColor(255,255,255);
                alert->show("Connection Error","unknown error");
            }
        }else{
            (other handle methods depend on different URL)
        }
}

Der Code ist lang und wird häufig verwendet. Für den obigen Code sind jedoch keine zusätzlichen Dinge wie benutzerdefinierte Funktion und Klasse erforderlich (HttpRequest und Alert werden beide standardmäßig vom Framework bereitgestellt). Obwohl das Codesegment lang ist, ist dies der Fall unkompliziert und nicht komplex (es ist lang, nur weil es Bündel von Einstellungen wie URL, Schriftgröße ... gibt), und das Codesegment weist geringe Unterschiede zwischen Klassen auf (z. B. URL, Anforderungsdaten, Fehlercode-Handle-Fälle, normales Handle) Fälle...)

Meine Frage ist, ist es akzeptabel, langen, aber unkomplizierten Code zu kopieren und einzufügen, anstatt sie in eine Funktion zu packen, um die Abhängigkeit von Code zu verringern?

ggrr
quelle
89
Stellen Sie sich vor, Sie haben einen Fehler in diesem Code, z. B., Sie geben von Ihnen zugewiesene Objekte nicht frei. (Befreit Ihr Framework die AlertObjekte?) Stellen Sie sich nun vor, Sie müssen jede kopierte Instanz dieses Codes finden, um den Fehler zu beheben. Stellen Sie sich nun vor, nicht Sie müssen es tun, sondern ein verrückter Axtmörder, der weiß, dass Sie derjenige waren, der all diese Kopien überhaupt erstellt hat.
Sebastian Redl
8
Übrigens ist das Mischen von Netzwerken und Fehleranzeige an einem Ort bereits ein großes No-No, IMHO.
sleske
11
Nein niemals. Völlig inakzeptabel. Wenn Sie an meinem Projekt teilnehmen würden, wären Sie nicht mehr an meinem Projekt und würden an einem Schulungsprogramm oder PIP teilnehmen.
nhgrif
10
Und dann kommt der Supervisor, der sagt: "Diese Alarmbox in der Mitte meines Bildschirms ist ein absoluter Schrecken. Ich schaue mir Katzenspiele an und das Popup blockiert meine Sicht jedes Mal, wenn sie auftaucht. Bitte schiebe es nach rechts oben." . " 3 Wochen später "Was zum Teufel hast du gemacht ?! Ich kann meine Katzen-JIFs nicht mehr schließen, weil IHR Pop-up das X oben rechts verdeckt. Repariere es."
MonkeyZeus
11
Ich denke, jeder hier scheint zu denken, dass dies eine schlechte Idee ist. Aber um die Frage umzudrehen, warum sollten Sie diesen Code NICHT in eine separate Klasse oder Funktion einordnen?
Karl Gjertsen

Antworten:

87

Sie müssen die Kosten der Änderung berücksichtigen. Was wäre, wenn Sie die Art und Weise, wie Verbindungen hergestellt werden, ändern möchten? Wie einfach wäre es? Wenn Sie viel duplizierten Code haben, kann es sehr zeitaufwändig und fehleranfällig sein, alle Stellen zu finden, die geändert werden müssen.

Sie müssen auch Klarheit berücksichtigen. Das Betrachten von 30 Codezeilen ist höchstwahrscheinlich nicht so einfach zu verstehen wie ein einzelner Aufruf einer "connectToInternet" -Funktion. Wie viel Zeit wird beim Versuch, den Code zu verstehen, verloren gehen, wenn neue Funktionen hinzugefügt werden müssen?

In bestimmten seltenen Fällen ist die Vervielfältigung kein Problem. Zum Beispiel, wenn Sie ein Experiment durchführen und der Code am Ende des Tages weggeworfen wird. Im Allgemeinen überwiegen die Kosten für die Vervielfältigung die geringe Zeitersparnis, wenn der Code nicht in eine separate Funktion gezogen werden muss .

Siehe auch https://softwareengineering.stackexchange.com/a/103235/63172

Vaughn Cato
quelle
19
... und wer weiß, ob diese 30 Zeilen wirklich die gleichen sind wie die anderen, die Sie zuvor angesehen haben, oder ob jemand aus irgendeinem Grund auf einen anderen Port oder eine andere IP-Adresse in seiner Kopie gewechselt ist. Die implizite Annahme, dass " ein Bündel von etwa 30 Zeilen, die mit dem beginnen, HttpRequestalle gleich sind ", ist ein leicht zu beweisender Irrtum.
null
@null Ausgezeichneter Punkt. Ich habe einmal an Code gearbeitet, bei dem Datenbankverbindungs-Hookups kopiert und überall eingefügt wurden, aber einige hatten geringfügige Unterschiede im Setup. Ich hatte keine Ahnung, ob es sich um wichtige, absichtliche Änderungen oder nur zufällige Unterschiede handelt
user949300
54

Nein.

Tatsächlich sollte sogar Ihr "einfacher" Code in kleinere Teile aufgeteilt werden. Mindestens zwei.

Eine, die die Verbindung herstellt und die normale Antwort von 200 verarbeitet. Was passiert zum Beispiel, wenn Sie in einigen Fällen von einem POST zu einem PUT wechseln? Was ist, wenn Sie zig Millionen dieser Verbindungen herstellen und Multithreading oder Verbindungspooling benötigen? Wenn Sie den Code mit einem Argument für die Methode an einer einzigen Stelle haben, wird dies viel einfacher

Ebenso eine andere, um Fehler zu behandeln. Zum Beispiel, wenn Sie die Farbe oder die Schriftgröße der Warnung ändern. Oder Sie haben Probleme mit zeitweiligen Verbindungen und möchten die Fehler protokollieren.

user949300
quelle
Sie könnten auch SRP zitieren: Mit einem Codeblock, der nur einen Zweck hat, ist es so viel einfacher zu verstehen und zu warten ..
Roland Tepp
Dies ist ein Fall, in dem DRY und SRP tatsächlich ausgerichtet sind. Manchmal tun sie es nicht.
user949300
18

ist es akzeptabel zu kopieren und einzufügen ...

Nein.

Für mich ist das entscheidende Argument dieses:

... es wird allgemein verwendet ...

Wenn Sie ein Stück Code in verwenden mehr als ein Ort , dann, wenn sie sich ändert, müssen Sie ändern es in mehr als einem Ort oder starten Sie Unstimmigkeiten zu bekommen - „seltsame Dinge“ zu passieren beginnen (dh Sie einführen Bugs).

es ist einfach und nicht komplex ...

Und so sollte es umso einfacher sein, eine Funktion umzugestalten.

... es gibt Bündel von Einstellungen wie URL, Schriftgröße ...

Und was tun Nutzer lieben zu ändern? Schriftarten, Schriftgrößen, Farben usw. usw.

Jetzt; An wie vielen Stellen müssen Sie denselben Code ändern, um sie alle wieder in derselben Farbe / Schriftart / Größe zu erhalten? (Empfohlene Antwort: nur ein ).

... das Codesegment weist geringe Unterschiede zwischen den Klassen auf (z. B. URL, Anforderungsdaten, Fehlercode-Handle-Fälle, normale Handle-Fälle ...)

Variation => Funktionsparameter.

Phill W.
quelle
"Und was lieben Benutzer zu ändern? Schriftarten, Schriftgrößen, Farben, etc" das ist so viel auf. Möchten Sie das wirklich an Dutzenden von Orten ändern?
Dan
8

Das hat eigentlich nichts mit Kopieren und Einfügen zu tun. Wenn Sie Code von einer anderen Stelle übernehmen, liegt es in der Sekunde, in der Sie den Code übernehmen, an Ihrem Code und Ihrer Verantwortung. Es spielt also keine Rolle, ob der Code vollständig von Ihnen selbst kopiert oder geschrieben wurde.

In Ihren Warnungen treffen Sie einige Entwurfsentscheidungen. Höchstwahrscheinlich sollten für alle Warnungen ähnliche Entwurfsentscheidungen getroffen werden. Es ist also wahrscheinlich, dass Sie eine Methode irgendwo "ShowAlertInAStyleSuitableForMyApplication" oder etwas kürzer haben sollten, und diese sollte aufgerufen werden.

Sie werden viele http-Anfragen mit ähnlicher Fehlerbehandlung haben. Sie sollten die Fehlerbehandlung wahrscheinlich nicht immer wieder duplizieren, sondern die allgemeine Fehlerbehandlung extrahieren. Vor allem, wenn Ihre Fehlerbehandlung etwas komplizierter wird (was ist mit Timeout-Fehlern, 401 usw.).

gnasher729
quelle
6

Die Vervielfältigung ist unter bestimmten Umständen in Ordnung. Aber nicht in diesem. Diese Methode ist zu komplex. Es gibt eine Untergrenze, bei der das Duplizieren einfacher ist als das "Ausklammern" einer Methode.

Beispielsweise:

def add(a, b)
    return a + b
end

ist doof, mach einfach a + b.

Aber wenn Sie nur ein kleines bisschen komplexer werden, sind Sie normalerweise weit davon entfernt.

foo.a + foo.b

soll werden

foo.total
def foo
    ...
    def total
        return self.a + self.b
    end
end

In Ihrem Fall sehe ich vier "Methoden". Wahrscheinlich in verschiedenen Klassen. Eine, um die Anfrage zu stellen, eine, um die Antwort zu erhalten, eine, um Fehler anzuzeigen, und eine Art Rückruf, der aufgerufen wird, nachdem die Antwort zurückgekehrt ist, um die Antwort zu verarbeiten. Ich persönlich würde wahrscheinlich noch eine Art "Wrapper" hinzufügen, um die Anrufe zu vereinfachen.

Um eine Webanfrage zu stellen, möchte ich, dass ein Anruf ungefähr so ​​aussieht:

Web.Post(URI, Params, ResponseHandler);

Diese Zeile würde ich überall in meinem Code haben. Wenn ich dann Änderungen an der Art und Weise vornehmen musste, wie ich Dinge bekomme, konnte ich dies schnell und mit viel weniger Aufwand tun.

Dies hält auch den Code trocken und hilft bei der SRP .

coteyr
quelle
0

In einem Projekt beliebiger Größe / Komplexität möchte ich in der Lage sein, Code zu finden, wenn ich ihn für die folgenden Zwecke benötige:

  1. Repariere es, wenn es kaputt ist
  2. Ändern Sie die Funktionalität
  3. Wiederverwenden

Wäre es nicht schön, sich einem laufenden Projekt anzuschließen oder mehrere Jahre an einem Projekt weiterzuarbeiten, und wenn eine neue Anfrage nach "Verbindung zum Internet herstellen und Verbindungsergebnisse anzeigen" an einem leicht zu findenden Ort aufgrund eines Gutes Design anstatt sich darauf zu verlassen, den gesamten Code nach einer http-Anfrage zu durchsuchen? Mit Google ist es wahrscheinlich ohnehin einfacher zu finden.

Keine Sorge, ich bin der Neue, und ich werde diesen Codeblock überarbeiten, weil ich so verärgert bin, dass ich mich diesem ahnungslosen Team mit der schrecklichen Codebasis angeschlossen habe, oder weil ich jetzt wie der Rest von unter großem Druck stehe Sie und werden es einfach kopieren und einfügen. Zumindest wird es den Chef von meinem Rücken abhalten. Wenn das Projekt wirklich als Katastrophe eingestuft wird, empfehle ich als Erster, es neu zu schreiben, indem Sie die Version in dem neuesten und besten Rahmen kopieren und einfügen, den keiner von uns versteht.

JeffO
quelle
0

Eine Klasse und / oder eine Funktion ist meiner Meinung nach besser. Ausnahmsweise wird die Datei dadurch kleiner, was ein großer Vorteil ist, wenn Sie mit Webanwendungen oder Anwendungen für Geräte mit wenig Speicher (IoT, ältere Telefone usw.) arbeiten.

Und natürlich ist der beste Punkt, dass Sie, wenn Sie aufgrund neuer Protokolle usw. etwas zu ändern haben, nur den Inhalt der Funktion ändern und diese Funktion nicht unzählige Male irgendwo ablegen, was sie möglicherweise sogar in verschiedenen Dateien noch schwieriger macht finden und ändern.

Ich habe einen ganzen SQL-Interpreter geschrieben, damit ich in PHP besser von MySQL auf MySQLi umsteigen kann , weil ich nur meinen Interpreter ändern muss und alles funktioniert, obwohl das ein extremes Beispiel ist.

My1
quelle
-1

Um zu entscheiden, ob ein Code dupliziert oder in eine Funktion verschoben werden soll, die zweimal aufgerufen wird, versuchen Sie, die wahrscheinlichere zu ermitteln:

  1. Es ist notwendig, beide Verwendungen des Codes auf dieselbe Weise zu ändern.

  2. Es ist erforderlich, mindestens eine Verwendung des Codes zu ändern, damit sie sich unterscheiden.

Im ersten Fall ist es wahrscheinlich besser, wenn eine Funktion beide Verwendungen verarbeitet. Im letzteren Fall ist es wahrscheinlich besser, einen separaten Code für die beiden Verwendungen zu haben.

Wenn Sie entscheiden, ob ein Code, der einmal verwendet wird, in eine andere Funktion geschrieben oder aus dieser herausgezogen werden soll, müssen Sie herausfinden, wie das erforderliche Verhalten der Funktion vollständig beschrieben wird. Wenn eine vollständige und genaue Beschreibung des für die Funktion erforderlichen Verhaltens genauso lang oder länger als der Code selbst ist, kann das Verschieben des Codes in eine separate Funktion das Verständnis der Dinge eher erschweren als erleichtern. Es kann sich dennoch lohnen, dies zu tun, wenn die Wahrscheinlichkeit groß ist, dass ein zweiter Anrufer dieselbe Funktion verwenden muss und künftige Änderungen an der Funktion beide Anrufer betreffen müssen. Wenn dies jedoch nicht berücksichtigt wird, würde die Lesbarkeit die Aufteilung der Elemente begünstigen auf die Ebene, auf der der Code und eine Beschreibung seines erforderlichen Verhaltens ungefähr gleich lang wären.

Superkatze
quelle