Hat es jemals Sinn für einen Refaktor, mit einem höheren LOC zu enden? [geschlossen]

25

Gibt es Fälle, in denen ausführlicherer Code (wie in logischeren Anweisungen) sauberer ist als präziserer Code?

falscher Benutzername
quelle
18
Natürlich tut es das. Auch wenn keine Duplikate vorhanden sind, nimmt der Code zum Definieren der neuen extrahierten Funktion eigenen Platz ein. Das Schreiben einer neuen Funktion kann vier Zeilen dauern, nur zwei Zeilen speichern und lohnt sich trotzdem.
Kilian Foth
5
"prägnanter Code"? Ich hasse die irrige Annahme, dass eine geringere Zeilenanzahl "besseren" Code bedeutet. Das tut es nicht. In der Tat ist es normalerweise das Gegenteil. Es kommt ein Punkt - und er ist sehr schnell erreicht - an dem es immer schwieriger wird, Code zu verstehen, wenn immer mehr Bedeutung in immer weniger Speicherplatz gesteckt wird . In der Tat gibt es einen ganzen Wettbewerb - Die International Obfuscated C Code Contest - wo viele Gewinner vertrauen auf jenen Grenzen des menschlichen Verstehens undurchdringlich Code zu schreiben.
Andrew Henle
1
Der Titel Ihrer Frage und Ihre Frage selbst stellen unterschiedliche Fragen. Zum Beispiel kann eine if-Anweisung in einen ternären Ausdruck geändert werden, der logisch identisch ist, jedoch nur eine Zeile enthält.
Captain Man
1
-1 *** Ausführlicherer Code (wie bei logischeren Anweisungen) *** Ausführlichkeit und die Anzahl der logischen Anweisungen sind zwei Dinge, die nichts miteinander zu tun haben. Es ist eine schlechte Form, Definitionen zu ändern.
Pieter B

Antworten:

71

Um das zu beantworten, nehmen wir ein reales Beispiel, das mir passiert ist. In C #, einer von mir verwalteten Bibliothek, hatte ich den folgenden Code:

TResult IConsFuncMatcher<T, TResult>.Result() =>
    TryCons(_enumerator) is var simpleMatchData && !simpleMatchData.head.HasValue
        ? _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
        : _recursiveConsTests.Any() 
            ? CalculateRecursiveResult() 
            : CalculateSimpleResult(simpleMatchData);

Bei der Diskussion mit Gleichaltrigen war man sich einig, dass die verschachtelten ternären Ausdrücke in Verbindung mit der "geschickten" Verwendung von is varknappem, aber schwer lesbarem Code resultierten.

Also habe ich es überarbeitet:

TResult IConsFuncMatcher<T, TResult>.Result()
{
    var simpleMatchData = TryCons(_enumerator);

    if (!simpleMatchData.head.HasValue)
    {
        return _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
    }

    return _recursiveConsTests.Any() 
        ? CalculateRecursiveResult() 
        : CalculateSimpleResult(simpleMatchData);
}

Die ursprüngliche Version enthielt nur einen zusammengesetzten Ausdruck mit einem Implizit return. Die neue Version enthält jetzt eine explizite Variablendeklaration, eine ifAnweisung und zwei explizite returns. Es enthält mehr Anweisungen und mehr Codezeilen. Alle, die ich konsultierte, hielten es jedoch für einfacher zu lesen und zu argumentieren, die Schlüsselaspekte von "sauberem Code" sind.

Die Antwort auf Ihre Frage lautet also nachdrücklich "Ja", ausführlicher kann sauberer sein als prägnanter Code und ist daher ein gültiges Refactoring.

David Arno
quelle
34
In der heutigen Zeit sind die Gehirne der Entwickler knapper als Festplatten, CPUs, Arbeitsspeicher oder Netzwerkbandbreiten. Diese anderen Dinge sind wichtig, und in bestimmten Anwendungen können sie Ihr begrenzender Faktor sein. In den meisten Fällen möchten Sie jedoch optimieren, damit die Entwickler zuerst den Code verstehen können , und dann diese anderen Dinge.
Anaximander
2
@ Anaximander, absolut einverstanden. Schreiben Sie Code, damit andere zuerst und der Compiler an zweiter Stelle lesen. Aus diesem Grund finde ich es nützlich, wenn andere meinen Code einer Peer-Review unterziehen, auch wenn ich der einzige bin, der ihn entwickelt.
David Arno
4
Wenn ich das überprüfen würde, würde ich vorschlagen, die Reihenfolge der Rückgabeanweisungen umzukehren und !die Bedingung zu entfernen . Ich würde auch vorschlagen, die zweite Rendite in eine else. Aber so wie es aussieht, ist es eine massive Verbesserung.
Martin Bonner unterstützt Monica
2
@DavidArno Ich sehe diese Logik, und wenn if (!foo.HasValue)es sich um eine Redewendung in Ihrem Code handelt, umso stärker. Allerdings ifist das nicht wirklich ein Exit-Early - es ist ein "dies oder das tun, je nachdem".
Martin Bonner unterstützt Monica
2
@fabric Der Vergleich von Booleschen Werten ist gefährlich. Ich vermeide es so oft ich kann.
Martin Bonner unterstützt Monica
30

1. Fehlende Korrelation zwischen LOC und Codequalität.

Ziel des Refactorings ist es, die Qualität eines Codeteils zu verbessern.

LOC ist eine sehr grundlegende Metrik, die manchmal mit der Qualität eines Codeteils korreliert. Beispielsweise ist es wahrscheinlich, dass bei einer Methode mit einigen Tausend LOC Qualitätsprobleme auftreten. Es ist jedoch zu beachten, dass LOC nicht die einzige Metrik ist und in vielen Fällen die Korrelation mit der Qualität fehlt. Beispielsweise ist eine 4-LOC-Methode nicht unbedingt besser lesbar oder besser wartbar als eine 6-LOC-Methode.

2. Einige Refactoring-Techniken bestehen aus dem Hinzufügen von LOCs.

Wenn Sie eine Liste von Refactoring-Techniken verwenden , können Sie leicht diejenigen erkennen, die darin bestehen, absichtlich LOCs hinzuzufügen . Beispiele:

Beide sind sehr nützliche Refactoring-Techniken, und ihre Auswirkung auf das LOC ist völlig irrelevant, wenn überlegt wird, ob sie verwendet werden sollen oder nicht.

Vermeiden Sie die Verwendung von LOC.

LOC ist eine gefährliche Metrik. Es ist sehr leicht zu messen und sehr schwer richtig zu interpretieren.

Bevor Sie nicht mit den Techniken zur Messung der Codequalität vertraut sind, sollten Sie zunächst darauf verzichten, den LOC zu messen. Meistens erhalten Sie nichts Relevantes, und es gibt Fälle, in denen Sie in die Irre geführt werden, die Qualität Ihres Codes zu verringern.

Arseni Mourzenko
quelle
Sie haben Ihre Antwort
überarbeitet
12

Wenn Sie das ultimative Ergebnis sehen möchten, indem Sie nur die Byteanzahl oder die LoC-Anzahl Ihres Quellcodes minimieren, sehen Sie sich die Einsendungen auf der Stack Exchange Code Golf-Website an .

Wenn Ihr Quellcode auf diese Weise reduziert wird, werden Sie bald ein unhaltbares Durcheinander haben. Selbst wenn Sie derjenige sind, der einen solchen Code geschrieben hat und ihn zu diesem Zeitpunkt vollständig verstanden hat, wie effizient werden Sie sein, wenn Sie in sechs Monaten wieder darauf zugreifen? Es gibt keine Beweise dafür, dass solch minimaler Code tatsächlich auch schneller ausgeführt wird.

Code sollte so geschrieben sein, dass jedes Mitglied Ihres Teams ihn sehen und sofort verstehen kann, was er tut.

Wanderer
quelle
Vielleicht überflüssig, aber nur um es herauszustellen; Wenn Sie Golf-Code aus Gründen der Lesbarkeit überarbeiten, erhalten Sie am Ende immer mehr LoC
JollyJoker 12.10.18
1

Ja, Refactoring kann definitiv zu mehr Codezeilen führen.

Der häufigste IMO-Fall ist, wenn Sie Code verwenden, der nicht generisch ist, und Sie ihn generischer / flexibler gestalten . Durch die einfache Generierung von Code werden die Codezeilen erheblich vergrößert (manchmal um den Faktor zwei oder mehr).

Wenn Sie davon ausgehen, dass der neu generierte Code von anderen (anstatt nur als interne Softwarekomponente) als Bibliothek verwendet wird, fügen Sie in der Regel unauffälligen Code und In-Code-Dokumentations-Markup hinzu, wodurch die Codezeilen erneut erhöht werden.

Zum Beispiel ist hier ein sehr häufiges Szenario, das für jeden Softwareentwickler auftritt:

  • Ihr Produkt benötigt dringend eine neue Funktion mit hoher Priorität oder eine Fehlerbehebung oder -verbesserung in zwei Wochen (oder in einem für Ihre Projektgröße / Unternehmensgröße / usw. als dringend erachteten Zeitraum).
  • Sie arbeiten hart und liefern XYZ pünktlich und es funktioniert. Herzliche Glückwünsche! Gut gemacht!
  • Während Sie XYZ entwickelten, unterstützte Ihr vorhandenes Code-Design / Ihre vorhandene Code-Implementierung XYZ nicht wirklich, aber Sie konnten XYZ in die Codebasis einbinden
  • Das Problem ist, dass das Shim hässlich ist und einen schrecklichen Code-Geruch hat, weil Sie einige knifflige / clevere / hässliche / schlechte Übungen gemacht haben, die aber irgendwie funktionieren
  • Wenn Sie später Zeit finden, überarbeiten Sie den Code, der möglicherweise viele Klassen ändert, oder fügen eine neue Klassenebene hinzu, und Ihre neue Lösung ist "richtig gemacht" und hat keinen schlechten Codegeruch mehr "Richtig" nimmt jetzt viel mehr Codezeilen in Anspruch.

Einige konkrete Beispiele, die mir auf den ersten Blick einfallen:

  • Für eine Befehlszeilenschnittstelle könnten Sie 5000 Zeilen if / else-if-Code haben, oder Sie könnten Rückrufe verwenden ... jeder Rückruf wäre viel kleiner und einfacher zu lesen / testen / verifizieren / debuggen / etc, aber wenn Sie Zeilen zählen Code Die hässlichen 5000 Zeilen von if / else-if-Code wären wahrscheinlich kleiner
  • Für ein Stück Code, das N Verarbeitungsmethoden unterstützt , könnten Sie erneut if / else-Anweisungen verwenden, die am hässlichsten aussehen würden ...
    • oder Sie könnten auf Callbacks umsteigen, die schöner / besser wären, aber Callbacks benötigen mehr Codezeilen (trotzdem Kompilierzeit)
    • Oder Sie können weiter abstrahieren und Plugins erstellen, die zur Laufzeit geändert werden können. Plugins sind nett, weil Sie das Hauptprodukt nicht für jedes neue Plugin oder jede Änderung an einem vorhandenen Plugin neu kompilieren müssen. und Sie können die API veröffentlichen, damit andere das Produkt erweitern können. ABER wieder verwendet ein Plugin-Ansatz mehr Codezeilen.
  • Für eine GUI erstellen Sie ein großartiges neues Widget
    • Sie oder ein Mitarbeiter stellen fest, dass das neue Widget für XYZ und ABC großartig wäre, aber im Moment ist das Widget eng integriert, um nur für XYZ zu funktionieren
    • Sie können das Widget so umgestalten, dass es für beide Anwendungen funktioniert. Jetzt wird die Gesamtzahl der Codezeilen erhöht
Trevor Boyd Smith
quelle