Verwendung von magischen Zeichenketten / Zahlen [geschlossen]

31

Dies ist ein etwas kontroverses Thema, und ich denke, es gibt ebenso viele Meinungen wie Programmierer. Aber um es besser zu machen, möchte ich wissen, wie es in der Wirtschaft (oder an Ihren Arbeitsplätzen) üblich ist.

An meinem Arbeitsplatz haben wir strenge Kodierungsrichtlinien. Ein Abschnitt davon ist den magischen Zeichenfolgen / Zahlen gewidmet. Es heißt (für C #):

Verwenden Sie in Ihrem Code keine literalen Werte, weder numerische Werte noch Zeichenfolgen, außer um symbolische Konstanten zu definieren. Verwenden Sie das folgende Muster, um Konstanten zu definieren:

public class Whatever  
{  
   public static readonly Color PapayaWhip = new Color(0xFFEFD5);  
   public const int MaxNumberOfWheels = 18;  
}

Es gibt Ausnahmen: Die Werte 0, 1 und null können fast immer sicher verwendet werden. Sehr oft sind auch die Werte 2 und -1 in Ordnung. Zeichenfolgen, die für die Protokollierung oder Ablaufverfolgung vorgesehen sind, sind von dieser Regel ausgenommen. Literale sind zulässig, wenn ihre Bedeutung aus dem Kontext klar hervorgeht und künftigen Änderungen nicht mehr unterworfen ist.

mean = (a + b) / 2; // okay  
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough

Eine ideale Situation wäre eine offizielle Studie, die Auswirkungen auf die Lesbarkeit / Wartbarkeit des Codes zeigt, wenn:

  • Magische Zahlen / Strings sind überall
  • Magische Zeichenfolgen / Zahlen werden vernünftigerweise (oder in unterschiedlichem Umfang) durch konstante Deklarationen ersetzt - und bitte schreien Sie mich nicht an, wenn Sie "vernünftigerweise" verwenden. Ich weiß, dass jeder eine andere Vorstellung davon hat, was "vernünftigerweise" ist
  • Magische Zeichenfolgen / Zahlen werden im Übermaß und an Stellen, an denen sie nicht sein müssten, ersetzt (siehe mein Beispiel unten).

Ich möchte dies tun, um einige wissenschaftlich fundierte Argumente zu haben, wenn ich mit einem meiner Kollegen diskutiere, der Konstanten wie die folgenden deklariert:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Ein anderes Beispiel wäre (und dieses ist in JavaScript):

var someNumericDisplay = new NumericDisplay("#Div_ID_Here");

Kleben Sie DOM-IDs auf Ihre Javascript-Datei, wenn diese ID nur an einer Stelle verwendet wird?

Ich habe die folgenden Themen gelesen:
StackExchange
StackOverflow
Bytes IT-Community
Es gibt viel mehr Artikel, und nach dem Lesen dieser Artikel tauchen einige Muster auf.

Meine Frage ist also, ob wir in unserem Code magische Zeichenfolgen und Zahlen verwenden sollen. Ich bin speziell auf der Suche nach kompetenten Antworten, die nach Möglichkeit durch Referenzen untermauert sind.

Daniel Gruszczyk
quelle
2
Eine magische Variable ist eine Variable mit einer Bedeutung, die sich nicht in ihrem Inhalt widerspiegelt. Der ganzzahlige Wert '10' spiegelt die Bedeutung der Zahl 10 wider, sodass keine Konstante erforderlich ist. Gleiches gilt für Leerzeichen und Semikolon. Wenn Sie dagegen den Wert '%% ?? %%' haben und dies ein benutzerdefiniertes Trennzeichen ist, muss dieses als Konstante platziert werden, da sein Inhalt nicht die Tatsache widerspiegelt, dass es ein Trennzeichen ist.
Jeroen Vannevel
23
NumberTen = 10Das ist sinnlos, da die Nummer 10 nicht neu definiert wird. MaxRetryCount = 10Das hat einen Punkt, an dem wir die maximale Anzahl der Wiederholungen ändern möchten. private const char SemiColon = ';'; Dumm. private const char LineTerminator = ';'; Clever.
Mike
1
Die eigentliche Frage ist nicht klar.
Tulains Córdova

Antworten:

89

... wenn ich mit einem meiner Kollegen streite, der so weit geht, Konstanten zu deklarieren wie:

private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Das Argument, das Sie mit Ihrem Kollegen vorbringen müssen, besteht nicht darin, ein wörtliches Leerzeichen zu benennen, Spacesondern in der schlechten Wahl des Namens für seine Konstanten.

Angenommen, Ihr Code analysiert einen Datenstrom, der Felder enthält, die durch Semikolons ( a;b;c) und durch Leerzeichen ( a;b;c d;e;f) voneinander getrennt sind . Wenn jemand, der Ihre Spezifikation geschrieben hat, Sie in einem Monat anruft und sagt: "Wir haben uns geirrt, die Felder in den Datensätzen sind durch Pipe-Symbole ( a|b|c d|e|f) getrennt." Was tun Sie?

Unter dem Wert-as-Namensschema Ihres Kollege vorzieht, dann würden Sie den Wert der wörtlichen (ändern müssen SemiColon = '|') und Live mit Code, der verwenden weiterhin SemiColonfür etwas , das nicht mehr wirklich ein Semikolon ist. Das führt zu negativen Kommentaren in Code Reviews . Um dies zu verringern, können Sie den Namen des Literales in PipeSymboländern und jedes Vorkommen von SemiColonin durchgehen und ändern PipeSymbol. In diesem Fall hätten Sie auch einfach ein Semikolon ( ';') verwenden können, da Sie jede Verwendung einzeln bewerten müssen und die gleiche Anzahl von Änderungen vornehmen müssen.

Bezeichner für Konstanten müssen beschreiben, was der Wert tut , nicht was der Wert ist , und dort hat Ihr Kollege eine Linkskurve ins Unkraut gemacht. In der oben beschriebenen Feldaufteilungsanwendung dient das Semikolon als Feldtrennzeichen, und die Konstanten sollten entsprechend benannt werden:

private const char FieldSeparator = ';';    // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;

Auf diese Weise ändern Sie beim Ändern des Feldtrennzeichens genau eine Codezeile, die Deklaration der Konstante. Jemand, der sich die Änderung ansieht, sieht nur diese eine Zeile und erkennt sofort, dass sich das Feldtrennzeichen von einem Semikolon in ein Pipe-Symbol geändert hat. Der Rest des Codes, der sich nicht ändern musste, weil er eine Konstante verwendete, bleibt derselbe, und der Leser muss nicht darin stöbern, um zu sehen, was noch getan wurde.

Blrfl
quelle
Ich bin völlig einverstanden. Vor einigen Jahrzehnten arbeitete ich an einem Projekt, bei dem Nachrichten in Segmenten mit jeweils 8 allgemeinen Registern gesendet wurden. Jemand hatte #define one 1 #define two 2 etc deklariert (oder was auch immer das Äquivalent war in UK Post Office Coral, der Sprache der Wahl). Es kam die #define one 8 #define two 16
Nachricht,
3
So albern Namen wie Semikolon oder PipeSymbol erscheinen, das Ändern der Namen mithilfe eines Skripts ist viel einfacher als das Ändern aller betroffenen Namen ;in |.
Brandin
Was ist mit dem Fall, in dem ein bestimmtes String-Literal oft in einer Datei verwendet wird, aber keine andere Bedeutung als den Wert hat? Wenn Sie beispielsweise testen, ob Sie in 20 verschiedenen Szenarien einen bestimmten Schlüssel in einer Karte erhalten können, sollte ich dann eine Konstante wie die folgende definieren: public static final String MY_KEY_NAME = "MyKeyName"
Jordan McQueen,
1
@JordanMcQueen Es gibt einen Fall für die Verwendung von nackten Literalen, wenn (und nur wenn) jedes genau einmal verwendet wird und nirgendwo anders benötigt wird. Wenn es so etwas wie jedes Wesen Code Szenario , das ein anderes Dateiformat verarbeitet, sollte jedes Format eine eigene Konstante definieren (zB CSV_RECORD_SEPARATOR, TSV_RECORD_SEPARATORusw.).
Blrfl
8

Die Definition von Semikolon als Konstante ist überflüssig, da Semikolon für sich genommen bereits konstant ist . Es wird sich nie ändern.

Es ist nicht so, dass irgendjemand eines Tages "Änderung der Terminologie, + ist jetzt das neue Semikolon" ankündigt , und Ihr Kollege wird sich gerne beeilen, nur um die Konstante zu aktualisieren (sie haben mich ausgelacht - schauen Sie sie sich jetzt an).

Es gibt auch eine Frage der Konsistenz. Ich garantiere, dass seine NumberTenKonstante NICHT von jedem verwendet wird (die meisten Programmierer sind nicht verrückt), so dass sie nicht dem Zweck dient, den sie ohnehin erwartet hatten. Wenn die Apokalypse kommt und "zehn" global auf 9 skaliert wird, reicht die Aktualisierung der Konstante NICHT aus, da Sie immer noch eine Menge wörtlicher 10s in Ihrem Code haben, sodass das System selbst innerhalb des Gültigkeitsbereichs völlig unvorhersehbar wird einer revolutionären Annahme, dass "zehn" "9" bedeutet.

Das Speichern aller Einstellungen als consts ist etwas, worüber ich mir auch Gedanken machen muss. Man sollte das nicht leichtfertig machen.

Welche Beispiele für diese Art der Nutzung haben wir bisher gesammelt? Zeilenabschluss ... Maximale Anzahl der Wiederholungsversuche ... Maximale Anzahl der Räder ... Sind wir sicher, dass sich diese niemals ändern werden?

Das Ändern der Standardeinstellungen erfordert eine Neukompilierung einer Anwendung und in einigen Fällen sogar ihrer Abhängigkeiten (da numerische Konstantenwerte während der Kompilierung möglicherweise fest codiert werden).

Es gibt auch den Test- und Spottaspekt. Sie haben die Verbindungszeichenfolge als const definiert, aber jetzt können Sie den Datenbankzugriff (Herstellen einer falschen Verbindung) in Ihrem Komponententest nicht verspotten.

Konrad Morawski
quelle
4
"Es wird sich nie ändern." Früher dachte ich über den Apostroph nach (für immer an den ASCII-Wert 39 gebunden). Einige alte Apps haben das Apostroph gelockt. Aber heutzutage behandeln moderne Apps diesen ASCII-Wert als ein einfaches Apostroph, das mit alten Apps kompatibel ist. Stattdessen wird häufig (linkes einfaches Anführungszeichen, Unicode 8217 ) für Apps verwendet, die mit einer anderen Glyphe für die gewellte Marke kompatibel sind. Da Europa Kommas verwendet, wie die Amerikaner Punkte als Dezimaltrennzeichen verwenden, zögere ich ein bisschen, "nicht ... jemals" zu deklarieren.
TOOGAM
@TOOGAM Nun, Ihr Beispiel rechtfertigt eine DecimalPointKonstante - aber nicht Commaoder PeriodKonstanten. Es ist ein ziemlicher Unterschied: Ersteres bezeichnet eine Funktion , eine Rolle oder einen Zweck des Wertes. "Semikolon" oder "Komma" fallen nicht unter diese Kategorie.
Konrad Morawski
Das gilt für das Dezimalpunkt-Beispiel. Das Apostroph-Beispiel scheint jedoch eine ähnliche (oder identische) Kategorie wie ein Komma (oder Semikolon) zu sein.
TOOGAM
@KonradMorawski Semikolon kann für viele Zwecke verwendet werden, z. B. zum Aufteilen der Zeichenfolge oder zum Beenden der Zeile. Es ist die Bedeutung (nicht der Wert), die für die Benennung der Konstanz verwendet werden soll. Berücksichtigen Sie die zukünftige Änderung, dh, wir lassen morgen 20 Datensätze verarbeiten, sodass die als NumberTen bezeichnete Konstanz aus dem Kontext gerät , während maxRecord noch in Ordnung wäre.
MaxZoom
5
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;

Ihr Kollege strebt also einen täglichen WTF-Eintrag an. Diese Definitionen sind albern und überflüssig. Wie jedoch von anderen betont wurde, wären die folgenden Definitionen nicht dumm oder überflüssig:

private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int  BalanceInquiryCode = 10;

"Magische" Zahlen und Zeichenfolgen sind Konstanten, deren Bedeutung über ihren unmittelbaren, wörtlichen Wert hinausgeht. Wenn die Konstante 10eine Bedeutung hat, die über "zehn Dinge" hinausgeht (beispielsweise als Code für eine bestimmte Operation oder einen bestimmten Fehlerzustand), wird sie zu "Magie" und sollte durch eine symbolische Konstante ersetzt werden, die diese abstrakte Bedeutung beschreibt.

Symbolische Konstanten beschreiben nicht nur die Absicht, sondern ersparen Ihnen auch Kopfschmerzen, wenn Sie ein Literal falsch schreiben. Eine einfache Umstellung von "CVS" auf "CSV" in einer Codezeile durchlief den gesamten Unit-Test und die Qualitätssicherung und schaffte es in die Produktion, wo ein bestimmter Vorgang fehlschlug. Ja, offensichtlich waren die Geräte- und QS-Tests unvollständig und das ist ein eigenes Problem, aber die Verwendung einer symbolischen Konstante hätte dieses bisschen Sodbrennen insgesamt vermieden.

John Bode
quelle
3

Daran sollte nichts Kontroverses sein. Es geht nicht darum, ob magische Zahlen verwendet werden sollen oder nicht, es geht darum, lesbaren Code zu haben.
Betrachten Sie den Unterschied zwischen: if(request.StatusCode == 1)und if(request.HasSucceeded). In diesem Fall würde ich behaupten, dass Letzteres weitaus besser lesbar ist, aber das bedeutet nicht, dass Sie niemals Code wie diesen haben können int MaxNumberOfWheels = 18.

PS: Deshalb hasse ich Kodierungsrichtlinien. Entwickler sollten ausgereift genug sein, um solche Urteilsforderungen zu stellen. Sie sollten es nicht einem Textstück überlassen, das von Gott weiß, wer gebildet wurde.

Stefan Billiet
quelle
13
Die Fahrer sollten reif genug sein, um beurteilen zu können, auf welcher Straßenseite sie fahren;)
Konrad Morawski
2
Das Ergebnis eines Entscheidungsaufrufs kann selbst bei erfahrenen Entwicklern variieren, sodass selbst willkürliche Codierungsrichtlinien die Lesbarkeit durch Konsistenz verbessern sollen. Dies hat nichts mit der Tatsache zu tun, dass das Erstellen einer konstanten NumberTen keinen Sinn ergibt.
Mike Partridge
1
Ich würde nicht darauf bestehen, dass sie formal, abgestempelt usw. sein müssen, sie können informell sein, aber sie sollten vereinbart werden, und dies geht bereits über die individuelle Urteilsfähigkeit hinaus. Aber Sie haben Ihren Kommentar jetzt gelöscht Stefan :)
Konrad Morawski
1
@StefanBilliet - überhaupt nicht. Mein Punkt ist, dass die Lesbarkeit durch Konsistenz verbessert wird. Das Problem hierbei ist nicht die Kodierungsrichtlinie selbst, sondern eine Richtlinie, die durch Missverständnisse auf die Spitze getrieben wird.
Mike Partridge
@ MikePartridge Vielleicht sollte ich ausgearbeitet haben; Die Kodierungsrichtlinien, die ich gesehen habe, spiegeln eher den Trend eines allgemeinen Regelwerks darüber wider, wie jemand irgendwo dachte, dass Software geschrieben werden sollte, als Vereinbarungen wie Sie und Konrad, an die er wahrscheinlich denkt :-)
Stefan Billiet