Soll ich große Funktionen umgestalten, die zumeist aus einer Regex bestehen? [geschlossen]


15

Ich habe gerade eine Funktion geschrieben, die ungefähr 100 Zeilen umfasst. Wenn Sie das hören, sind Sie wahrscheinlich versucht, mich über einzelne Verantwortlichkeiten zu informieren und mich zur Umgestaltung aufzufordern. Das ist auch mein Bauchgefühl, aber hier ist das Problem: Die Funktion macht eine Sache. Es führt eine komplexe Zeichenfolgenmanipulation durch, und der Funktionskörper besteht hauptsächlich aus einer ausführlichen Regex, die in viele dokumentierte Zeilen unterteilt ist. Wenn ich den regulären Ausdruck in mehrere Funktionen aufteilen würde, hätte ich das Gefühl, die Lesbarkeit zu verlieren , da ich effektiv die Sprache wechsle und nicht in der Lage bin, einige Funktionen des regulären Ausdrucks zu nutzen. Hier ist jetzt meine Frage:

Sind große Funktionskörper bei der String-Manipulation mit regulären Ausdrücken noch ein Anti-Pattern? Es scheint, dass benannte Erfassungsgruppen Funktionen einen sehr ähnlichen Zweck erfüllen. Übrigens habe ich Tests für jeden Fluss durch den Regex.


3
Ich glaube nicht, dass irgendetwas an Ihrer Funktion falsch ist, wenn man bedenkt, dass ein großer Teil davon Dokumentation ist . Es kann jedoch ein Wartbarkeitsproblem bei der Verwendung eines großen regulären Ausdrucks geben.
Joel Cornett

2
Sind Sie sicher, dass ein Riesen-Regex die beste Lösung für Ihr Problem ist? Haben Sie über einfachere Alternativen nachgedacht, wie eine Parser-Bibliothek oder das Ersetzen eines benutzerdefinierten Dateiformats durch ein Standardformat (XML, JSON usw.)?
Lortabac

2
Gibt es andere Funktionen, die eine geänderte / verbesserte / vereinfachte Version dieses Regex verwenden? Das wäre ein wichtiger Indikator dafür, dass ein Refactoring stattfinden sollte. Wenn nicht, würde ich es so lassen, wie es auch ist. Eine komplexe String-Manipulation wie diese zu benötigen, ist für sich genommen eine gelbe Flagge (nun, ich kenne den Kontext nicht, daher nur gelb), und die Funktion nach unten umzugestalten, scheint mir eher ein Ritual zu sein, um die Schuldgefühle loszuwerden it;)
Konrad Morawski

8
Wie kann ein 100 Zeilen Regexp nur 1 Sache machen?
Pieter B

@Lortabac: Die Eingabe ist benutzergenerierter Text (Prosa.)
DudeOnRock

Antworten:


36

Was Ihnen begegnet, ist die kognitive Dissonanz, die entsteht, wenn Sie Leuten zuhören, die die sklavische Einhaltung von Richtlinien unter dem Deckmantel von "Best Practices" gegenüber begründeten Entscheidungen bevorzugen.

Sie haben Ihre Hausaufgaben klar gemacht:

  • Der Zweck der Funktion wird verstanden.
  • Die Funktionsweise seiner Implementierung wird verstanden (dh lesbar).
  • Es gibt flächendeckende Tests der Implementierung.
  • Diese Tests bestehen, was bedeutet, dass Sie der Meinung sind, dass die Implementierung korrekt ist.

Wenn einer dieser Punkte nicht zutrifft, würde ich als Erster sagen, dass Ihre Funktion Arbeit braucht. Es gibt also eine Stimme dafür, dass der Code unverändert bleibt.

Die zweite Stimme ergibt sich aus der Betrachtung Ihrer Optionen und dem, was Sie jeweils erhalten (und verlieren):

  • Refactor. Auf diese Weise können Sie die Vorstellung einer Person, wie lange eine Funktion dauern soll, einhalten und die Lesbarkeit beeinträchtigen.
  • Nichts tun. Dadurch bleibt die Lesbarkeit erhalten und die Übereinstimmung mit der Vorstellung einer Person, wie lange eine Funktion dauern sollte, wird beeinträchtigt.

Diese Entscheidung hängt davon ab, worauf Sie mehr Wert legen: Lesbarkeit oder Länge. Ich falle in das Lager, das die Länge für gut hält, aber die Lesbarkeit ist wichtig und wird die letztere jeden Tag in der Woche überholen.

Fazit: Wenn es nicht kaputt ist, reparieren Sie es nicht.


10
+1 für "Wenn es nicht kaputt ist, repariere es nicht."
Giorgio

Tatsächlich. Sandy Metz Regeln ( gist.github.com/henrik/4509394 ) sind nett und alle, aber unter youtube.com/watch?v=VO-NvnZfMA4#t=1379 spricht sie darüber, wie sie entstanden sind und warum die Leute sie nehmen sie viel zu ernst.
Amadan

@Amdan: Mit dem zusätzlichen Kontext aus dem Video macht es Sinn, was Metz getan hat. Ihre Empfehlung an diesen einen Kunden war an einem Ende absichtlich extrem, um dem am anderen Ende extremen Verhalten entgegenzuwirken und es in die vernünftigere Mitte zu ziehen. Der Rest dieser Diskussion läuft auf die Richtschnur meiner Antwort hinaus: Denken, nicht Glauben, ist der Weg, um die beste Vorgehensweise zu bestimmen.
Blrfl

19

Ehrlich gesagt kann Ihre Funktion "eine Sache tun", aber wie Sie selbst angegeben haben

Ich könnte anfangen, die Regex in mehrere Funktionen aufzuteilen,

was bedeutet, dass Ihr Reg-Ex-Code viele Dinge tut. Und ich denke, es könnte in kleinere, einzeln testbare Einheiten zerlegt werden. Wenn dies jedoch eine gute Idee ist, ist die Antwort nicht einfach (insbesondere ohne den tatsächlichen Code zu sehen). Und die richtige Antwort kann weder "ja" noch "nein" sein, sondern "noch nicht, aber beim nächsten Mal müssen Sie etwas in dieser Ausrichtung ändern exp".

aber ich fühle mich, als würde ich auf diese Weise die Lesbarkeit verlieren, da ich effektiv die Sprache wechsle

Und das ist der Kernpunkt - Sie haben einen Code, der in der Sprache reg ex geschrieben ist . Diese Sprache bietet kein gutes Mittel zur Abstraktion an sich (und ich betrachte "benannte Erfassungsgruppen" nicht als Ersatz für Funktionen). Ein Refactoring "in der Reg-Ex-Sprache" ist also nicht wirklich möglich, und das Verweben kleinerer Reg-Exps mit der Host-Sprache verbessert möglicherweise nicht die Lesbarkeit (zumindest haben Sie das Gefühl , aber Sie haben Zweifel, sonst hätten Sie die Frage nicht gestellt). . Also hier ist mein Rat

  • Zeigen Sie Ihren Code einem anderen fortgeschrittenen Entwickler (möglicherweise unter /codereview// ), um sicherzustellen, dass andere die Lesbarkeit auf Ihre Weise beurteilen. Seien Sie offen für die Idee, dass andere Benutzer eine 100-Zeilen-Registrierung möglicherweise nicht so lesbar finden wie Sie. Manchmal kann die Vorstellung von "es ist nicht leicht in kleinere Stücke zu zerbrechen" nur durch ein zweites Paar Augen überwunden werden.

  • Beobachten Sie die tatsächliche Evolvabilität. Sieht Ihre glänzende reg exp immer noch so gut aus, wenn neue Anforderungen eintreffen und Sie sie implementieren und testen müssen? Solange Ihre Registrierung gültig ist, würde ich es nicht anfassen, aber wenn etwas geändert werden muss, würde ich überlegen, ob es wirklich eine gute Idee ist, alles in diesen einen großen Block zu packen - und (im Ernst!) Zu überdenken, wenn sich jemand aufteilt kleinere Stücke wären keine bessere Option.

  • Beobachten Sie die Wartbarkeit - können Sie die reg exp in der aktuellen Form sehr gut debuggen? Besitzen Sie einen reg exp-Debugger, der Ihnen hilft, die Ursache zu finden, besonders nachdem Sie etwas ändern müssen und jetzt Ihre Tests Ihnen sagen, dass etwas nicht stimmt? Wenn das Debuggen schwierig wird, ist dies auch eine Gelegenheit, Ihr Design zu überdenken.


Ich würde sagen, dass benannte Erfassungsgruppen (Erfassungsgruppen im Allgemeinen) den Variablen final / write-once oder vielleicht den Makros am ähnlichsten sind. Mit ihnen können Sie auf bestimmte Teile der Übereinstimmung verweisen, entweder auf das vom Regex-Prozessor zurückgegebene Übereinstimmungsobjekt oder später im regulären Ausdruck.
JAB

4

Manchmal ist eine längere Funktion, die eine Sache erledigt, die geeignetste Art, eine Arbeitseinheit zu handhaben. Sie können leicht auf sehr lange Funktionen zugreifen, wenn Sie mit der Abfrage einer Datenbank beginnen (mit Ihrer bevorzugten Abfragesprache). Eine Funktion (oder Methode) lesbarer zu machen, während sie auf den angegebenen Zweck beschränkt ist, würde ich als das wünschenswerteste Ergebnis einer Funktion betrachten.

Die Länge ist ein beliebiger "Standard", wenn es um die Codegröße geht. Wenn eine 100-Zeilen-Funktion in C # als länglich angesehen werden kann, ist sie in einigen Assemblyversionen winzig. Ich habe einige SQL-Abfragen gesehen, die weit über den Bereich von 200 Codezeilen lagen und einen sehr komplizierten Datensatz für einen Bericht zurückgaben.

Fully - Code arbeiten , das ist so einfach wie möglich vernünftig machen es zum Ziel.

Ändere es nicht, nur weil es lang ist.


3

Sie können den regulären Ausdruck jederzeit in untergeordnete reguläre Ausdrücke aufteilen und den endgültigen Ausdruck nach und nach erstellen. Dies könnte das Verständnis für ein sehr großes Muster erleichtern, insbesondere wenn dasselbe Untermuster viele Male wiederholt wird. Zum Beispiel in Perl;

my $start_re = qr/(?:\w+\.\w+)/;
my $middle_re = qr/(?:DOG)|(?:CAT)/;
my $end_re = qr/ => \d+/;

my $final_re = $start_re . $middle_re . $end_re;
# or: 
# my $final_re = qr/${start_re}${middle_re}${end_re}/

Ich verwende die ausführliche Flagge, die noch praktischer ist als das, was Sie vorschlagen.
DudeOnRock

1

Ich würde sagen, brecht es, wenn es zerbrechlich ist. Unter dem Gesichtspunkt der Wartbarkeit und möglicherweise der Wiederherstellbarkeit ist es sinnvoll, diese zu unterbrechen, aber natürlich müssen Sie Ihre Funktion berücksichtigen, wie Sie Eingaben erhalten und was sie zurückgeben wird.

Ich erinnere mich, dass ich daran gearbeitet habe, Streaming-Chunked-Daten in Objekte zu zerlegen. Also habe ich es im Grunde genommen in zwei Hauptteile aufgeteilt, eine vollständige Zeichenfolgeeinheit aus codiertem Text erstellt und im zweiten Teil diese Einheiten in ein Datenwörterbuch geparst und organisiert sie (können zufällige Eigenschaften für verschiedene Objekte sein) und dann Objekte aktualisieren oder erstellen.

Außerdem konnte ich jeden Hauptteil in mehrere kleinere und spezifischere Funktionen aufteilen, so dass ich am Ende 5 verschiedene Funktionen hatte, um das Ganze zu erledigen, und einige der Funktionen an verschiedenen Stellen wiederverwenden konnte.


1

Eine Sache, die Sie vielleicht in Betracht gezogen haben oder nicht, ist, einen kleinen Parser in der von Ihnen verwendeten Sprache zu schreiben, anstatt einen regulären Ausdruck in dieser Sprache zu verwenden. Dies ist möglicherweise einfacher zu lesen, zu testen und zu warten.


Ich habe selbst darüber nachgedacht. Das Problem ist, dass die Eingabe Prosa ist und ich Hinweise aus dem Kontext und der Formatierung nehme. Wenn es möglich ist, einen Parser für so etwas zu schreiben, würde ich gerne mehr darüber erfahren! Ich konnte selbst nichts finden.
DudeOnRock

1
Wenn ein regulärer Ausdruck es analysieren kann, können Sie es analysieren. Ihre Antwort lässt mich den Eindruck erwecken, dass Sie sich mit dem Parsen nicht auskennen. Wenn das der Fall ist, möchten Sie vielleicht bei der Regex bleiben. Entweder das oder eine neue Fähigkeit erlernen.
Thomas Eding

Ich würde gerne eine neue Fähigkeit erlernen. Gibt es gute Ressourcen, die Sie vorschlagen können? Ich interessiere mich auch für die Theorie dahinter.
DudeOnRock

1

Riesen-Regexes sind in den meisten Fällen eine schlechte Wahl. Nach meiner Erfahrung werden sie häufig verwendet, weil der Entwickler mit dem Parsen nicht vertraut ist (siehe die Antwort von Thomas Eding ).

Angenommen, Sie möchten sich an eine auf Regex basierende Lösung halten.

Da ich den tatsächlichen Code nicht kenne, werde ich die beiden möglichen Szenarien untersuchen:

  • Der reguläre Ausdruck ist einfach (viele wörtliche Übereinstimmungen und wenige Alternativen)

    In diesem Fall sind die erweiterten Funktionen eines einzelnen regulären Ausdrucks nicht unbedingt erforderlich. Dies bedeutet, dass Sie wahrscheinlich von der Aufteilung profitieren werden.

  • Der reguläre Ausdruck ist komplex (viele Alternativen)

    In diesem Fall können Sie keine vollständige Testabdeckung erzielen, da Sie wahrscheinlich Millionen von möglichen Flows haben. Um es zu testen, müssen Sie es aufteilen.

Mir fehlt vielleicht die Vorstellungskraft, aber ich kann mir keine reale Situation vorstellen, in der ein Regex mit 100 Zeilen eine gute Lösung ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.