Was ist eine magische Zahl?
Warum sollte sie vermieden werden?
Gibt es Fälle, in denen dies angemessen ist?
Was ist eine magische Zahl?
Warum sollte sie vermieden werden?
Gibt es Fälle, in denen dies angemessen ist?
Eine magische Zahl ist eine direkte Verwendung einer Zahl im Code.
Zum Beispiel, wenn Sie (in Java):
public class Foo {
public void setPassword(String password) {
// don't do this
if (password.length() > 7) {
throw new InvalidArgumentException("password");
}
}
}
Dies sollte umgestaltet werden in:
public class Foo {
public static final int MAX_PASSWORD_SIZE = 7;
public void setPassword(String password) {
if (password.length() > MAX_PASSWORD_SIZE) {
throw new InvalidArgumentException("password");
}
}
}
Es verbessert die Lesbarkeit des Codes und ist leichter zu pflegen. Stellen Sie sich den Fall vor, dass ich die Größe des Passwortfeldes in der GUI festlege. Wenn ich eine magische Zahl verwende, muss ich jedes Mal, wenn sich die maximale Größe ändert, an zwei Stellen im Code Änderungen vornehmen. Wenn ich eine vergesse, führt das zu Inkonsistenzen.
Das JDK ist voll von Beispielen wie in Integer
, Character
y Math
Klassen.
PS: Statische Analysewerkzeuge wie FindBugs und PMD erkennen die Verwendung von magischen Zahlen in Ihrem Code und schlagen eine Umstrukturierung vor.
Eine magische Zahl ist ein fest kodierter Wert, der sich zu einem späteren Zeitpunkt ändern kann, aber deshalb schwer zu aktualisieren ist.
Nehmen wir zum Beispiel an, Sie haben eine Seite, die die letzten 50 Aufträge auf einer Übersichtsseite "Ihre Aufträge" anzeigt. 50 ist hier die magische Zahl, denn sie ist nicht durch einen Standard oder eine Konvention festgelegt, sondern eine Zahl, die Sie aus den in der Spezifikation genannten Gründen erfunden haben.
Jetzt haben Sie die 50 an verschiedenen Stellen - Ihr SQL-Skript ( SELECT TOP 50 * FROM orders
), Ihre Website (Ihre letzten 50 Bestellungen), Ihr Bestell-Login ( for (i = 0; i < 50; i++)
) und möglicherweise viele andere Orte.
Was passiert nun, wenn jemand beschließt, 50 in 25? oder 75? oder 153? zu ändern? Sie müssen nun die 50 an allen Stellen ersetzen, und es ist sehr wahrscheinlich, dass Sie sie übersehen. Suchen/Ersetzen funktioniert möglicherweise nicht, weil 50 für andere Dinge verwendet werden kann, und das blinde Ersetzen von 50 durch 25 kann einige andere schlechte Nebenwirkungen haben (z. B. Ihre Session.Timeout = 50
Aufruf, der ebenfalls auf 25 eingestellt ist, und die Benutzer beginnen, zu häufige Timeouts zu melden).
Außerdem kann der Code schwer zu verstehen sein, z. B. " if a < 50 then bla
"Wenn Sie mitten in einer komplizierten Funktion darauf stoßen, fragen sich andere Entwickler, die mit dem Code nicht vertraut sind, vielleicht "WTF is 50???"
Deshalb ist es am besten, solche zweideutigen und willkürlichen Zahlen an genau 1 Stelle zu haben - " const int NumOrdersToDisplay = 50
", denn das macht den Code besser lesbar (" if a < NumOrdersToDisplay
"Das bedeutet auch, dass man sie nur an einer genau definierten Stelle ändern muss.
Orte, an denen Magische Zahlen angebracht sind, sind alles, was durch eine Norm definiert ist, d. h. SmtpClient.DefaultPort = 25
o TCPPacketSize = whatever
(ich bin nicht sicher, ob das genormt ist). Auch alles, was nur innerhalb einer Funktion definiert ist, könnte akzeptabel sein, aber das hängt vom Kontext ab.
Haben Sie einen Blick auf den Wikipedia-Eintrag für magische Zahl?
Sie geht ein wenig ins Detail über alle Arten der Bezugnahme auf die magische Zahl. Hier ist ein Zitat über magische Zahlen als schlechte Programmierpraxis
Der Begriff "magische Zahl" bezieht sich auch auf die schlechte Programmierpraxis, Zahlen ohne Erklärung direkt im Quellcode zu verwenden. In den meisten Fällen erschwert dies die Lesbarkeit, das Verständnis und die Wartung von Programmen. Obwohl die meisten Leitfäden eine Ausnahme für die Zahlen Null und Eins machen, ist es eine gute Idee, alle anderen Zahlen im Code als benannte Konstanten zu definieren.
Magie: Unbekannte Semantik
Symbolische Konstante -> Bietet sowohl die richtige Semantik als auch den richtigen Kontext für die Verwendung
Semantisch: Die Bedeutung oder der Zweck einer Sache.
"Erstelle eine Konstante, benenne sie nach der Bedeutung und ersetze die Zahl durch sie." -- Martin Fowler
Erstens: Magische Zahlen sind nicht einfach nur Zahlen. Jeder Grundwert kann "magisch" sein. Bei den Basiswerten handelt es sich um manifeste Einheiten wie Ganzzahlen, Reals, Doubles, Floats, Datumsangaben, Strings, Booleans, Zeichen und so weiter. Das Problem ist nicht der Datentyp, sondern der "magische" Aspekt des Wertes, wie er in unserem Code erscheint.
Was verstehen wir unter "Magie"? Um genau zu sein: Mit "Magie" wollen wir auf die Semantik (Bedeutung oder Zweck) des Wertes im Kontext unseres Codes hinweisen; darauf, dass er unbekannt, unerkennbar, unklar oder verwirrend ist. Dies ist der Begriff der "Magie". Ein Basiswert ist nicht magisch, wenn seine semantische Bedeutung oder sein Zweck aus dem umgebenden Kontext ohne besondere Hilfsmittel (z. B. symbolische Konstante) schnell und einfach bekannt, klar und verständlich (nicht verwirrend) ist.
Daher ermitteln wir magische Zahlen, indem wir die Fähigkeit eines Codelesers messen, die Bedeutung und den Zweck eines Basiswerts aus dem umgebenden Kontext zu erkennen, zu verstehen und zu deuten. Je weniger bekannt, unklarer und verwirrter der Leser ist, desto "magischer" ist der Grundwert.
Wir haben zwei Szenarien für unsere magischen Grundwerte. Nur das zweite ist für Programmierer und Code von primärer Bedeutung:
Eine übergreifende Abhängigkeit von "Magie" besteht darin, dass der einzelne Grundwert (z. B. eine Zahl) keine allgemein bekannte Semantik hat (wie Pi), sondern eine lokal bekannte Semantik (z. B. Ihr Programm), die aus dem Kontext heraus nicht ganz klar ist oder in guten oder schlechten Kontexten missbraucht werden könnte.
Die Semantik der meisten Programmiersprachen lässt es nicht zu, einsame Grundwerte zu verwenden, es sei denn (vielleicht) als Daten (d. h. Datentabellen). Wenn wir auf "magische Zahlen" stoßen, tun wir dies im Allgemeinen in einem Kontext. Daher ist die Antwort auf
"Ersetze ich diese magische Zahl durch eine symbolische Konstante?"
ist:
"Wie schnell können Sie die semantische Bedeutung des Textes beurteilen und verstehen? Zahl (den Zweck ihres Vorhandenseins) in ihrem Kontext erfassen?"
Mit diesem Gedanken im Hinterkopf können wir schnell erkennen, dass eine Zahl wie Pi (3,14159) keine "magische Zahl" ist, wenn sie in den richtigen Kontext gestellt wird (z. B. 2 x 3,14159 x Radius oder 2*Pi*r). Hier wird die Zahl 3,14159 mental als Pi ohne die symbolische Konstantenkennung erkannt.
Dennoch ersetzen wir im Allgemeinen 3,14159 durch einen symbolischen konstanten Bezeichner wie Pi wegen der Länge und Komplexität der Zahl. Die Aspekte der Länge und Komplexität von Pi (in Verbindung mit dem Bedürfnis nach Genauigkeit) bedeuten in der Regel, dass der symbolische Bezeichner oder die Konstante weniger fehleranfällig ist. Die Erkennung von "Pi" als Name ist lediglich ein praktischer Bonus, aber nicht der Hauptgrund für die Verwendung der Konstante.
Lassen wir allgemeine Konstanten wie Pi beiseite und konzentrieren wir uns in erster Linie auf Zahlen, die eine besondere Bedeutung haben, die aber auf das Universum unseres Softwaresystems beschränkt ist. Eine solche Zahl könnte "2" sein (als grundlegender ganzzahliger Wert).
Wenn ich die Zahl 2 als solche verwende, könnte meine erste Frage lauten: Was bedeutet "2"? Die Bedeutung der "2" allein ist unbekannt und kann ohne Kontext nicht erkannt werden, so dass ihre Verwendung unklar und verwirrend ist. Auch wenn die "2" in unserer Software aufgrund der Sprachsemantik nicht vorkommen wird, wollen wir doch sehen, dass die "2" für sich allein keine besondere Semantik oder einen offensichtlichen Zweck hat.
Setzen wir unsere einsame "2" in einen Kontext von: padding := 2
, wobei der Kontext ein "GUI-Container" ist. In diesem Kontext bietet uns die Bedeutung von 2 (als Pixel oder andere grafische Einheit) einen schnellen Hinweis auf seine Semantik (Bedeutung und Zweck). Wir könnten hier aufhören und sagen, dass 2 in diesem Kontext in Ordnung ist und wir nichts weiter wissen müssen. Aber vielleicht ist das in unserem Software-Universum nicht die ganze Geschichte. Es steckt mehr dahinter, aber "padding = 2" als Kontext kann es nicht aufdecken.
Nehmen wir weiter an, dass 2 als Pixel-Padding in unserem Programm in unserem gesamten System von der Sorte "default_padding" ist. Wenn wir also die Anweisung padding = 2
ist nicht gut genug. Der Begriff "Standard" wird nicht offenbart. Nur wenn ich schreibe: padding = default_padding
als Kontext und dann anderswo: default_padding = 2
erkenne ich eine bessere und umfassendere Bedeutung (Semantik und Zweck) von 2 in unserem System.
Das obige Beispiel ist ziemlich gut, weil "2" an sich alles sein könnte. Nur wenn wir den Bereich und die Domäne des Verstehens auf "mein Programm" beschränken, wobei 2 die default_padding
in den GUI-UX-Teilen "meines Programms", machen wir endlich den Sinn von "2" in seinem richtigen Kontext. Hier ist "2" eine "magische" Zahl, die in eine symbolische Konstante zerlegt wird default_padding
im Kontext der GUI UX von "meinem Programm", um es so zu nutzen default_padding
schnell im größeren Kontext des beigefügten Codes zu verstehen.
Somit ist jeder Grundwert, dessen Bedeutung (Semantik und Zweck) nicht ausreichend und schnell verstanden werden kann, ein guter Kandidat für eine symbolische Konstante anstelle des Grundwertes (z. B. magische Zahl).
Auch Zahlen auf einer Skala können eine Bedeutung haben. Nehmen wir zum Beispiel an, wir entwickeln ein D&D-Spiel, in dem wir den Begriff des Monsters haben. Unser Monsterobjekt hat eine Eigenschaft namens life_force
was eine ganze Zahl ist. Die Zahlen haben Bedeutungen, die ohne Worte, die eine Bedeutung liefern, nicht bekannt oder klar sind. Wir beginnen also mit einer willkürlichen Aussage:
Anhand der oben genannten symbolischen Konstanten können wir uns ein Bild von der Lebendigkeit, der Tödlichkeit und der "Untotheit" (und den möglichen Auswirkungen oder Konsequenzen) unserer Monster in unserem D&D-Spiel machen. Ohne diese Worte (symbolische Konstanten) bleiben uns nur die Zahlen, die von -10 .. 10
. Nur der Bereich ohne die Worte lässt uns in einem Ort der möglicherweise große Verwirrung und möglicherweise mit Fehlern in unserem Spiel, wenn verschiedene Teile des Spiels haben Abhängigkeiten davon, was dieser Bereich von Zahlen bedeutet, um verschiedene Operationen wie attack_elves
o seek_magic_healing_potion
.
Deshalb wollen wir bei der Suche nach "magischen Zahlen" und deren Ersetzung sehr zielgerichtete Fragen über die Zahlen im Kontext unserer Software stellen und sogar darüber, wie die Zahlen semantisch miteinander interagieren.
Gehen wir noch einmal die Fragen durch, die wir stellen sollten:
Sie könnten eine magische Zahl haben, wenn ...
Untersuchen Sie die Grundwerte der eigenständigen Manifestkonstanten in Ihrem Codetext. Stellen Sie jede Frage langsam und mit Bedacht zu jedem Beispiel eines solchen Wertes. Überlegen Sie, wie aussagekräftig Ihre Antwort ist. Oft ist die Antwort nicht schwarz-weiß, sondern hat Schattierungen, die von der missverstandenen Bedeutung und dem Zweck, der Lerngeschwindigkeit und der Geschwindigkeit des Verständnisses abhängen. Es ist auch wichtig zu sehen, wie sie mit der sie umgebenden Software-Maschine zusammenhängt.
Letztendlich ist die Antwort auf die Frage nach der Ersetzung die Antwort auf die Frage nach der Stärke oder Schwäche des Lesers, die Verbindung herzustellen (z. B. "es zu verstehen"). Je schneller sie Sinn und Zweck verstehen, desto weniger "Magie" haben Sie.
SCHLUSSFOLGERUNG: Ersetzen Sie Grundwerte nur dann durch symbolische Konstanten, wenn die Magie groß genug ist, um schwer zu entdeckende Fehler aufgrund von Verwechslungen zu verursachen.
Eine magische Zahl ist eine Zeichenfolge am Anfang eines Dateiformats oder eines Protokollaustauschs. Diese Zahl dient als Sicherheitsprüfung.
Beispiel: Wenn Sie eine beliebige GIF-Datei öffnen, sehen Sie ganz am Anfang: GIF89. "GIF89" ist die magische Zahl.
Andere Programme können die ersten paar Zeichen einer Datei lesen und GIFs richtig identifizieren.
Die Gefahr besteht darin, dass zufällige Binärdaten dieselben Zeichen enthalten können. Das ist aber sehr unwahrscheinlich.
Was den Protokollaustausch betrifft, so können Sie damit schnell feststellen, dass die aktuelle "Nachricht", die Ihnen übermittelt wird, beschädigt oder ungültig ist.
Magische Zahlen sind immer noch nützlich.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.