Ist dies die Verwendung eines symbolischen konstanten Overkills?

34

Ich bin ziemlich neu in der Softwareentwicklung und habe als Lernübung ein Schachspiel geschrieben. Mein Freund schaute es sich an und wies darauf hin, dass mein Code so aussah

for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){

während er darauf bestand, dass es stattdessen sein sollte

for (int i = 0; i < CHESS_CONST; i++){
    for (int j = 0; j < CHESS_CONST; j++){

mit einem besseren Symbolnamen, an den ich im Moment gar nicht denken kann.

Jetzt weiß ich natürlich, dass ich es generell vermeiden soll, magische Zahlen zu verwenden, aber ich fühle mich wie seitdem

  1. Diese Zahl wird sich nie ändern.
  2. der Name konnte sowieso nicht so aussagekräftig sein, da die Nummer an so vielen Stellen im Code verwendet wird; und
  3. Jeder, der den Quellcode für ein Schachprogramm durchläuft, sollte genug über Schach wissen, um zu wissen, wofür die 8 ist.

Es ist wirklich keine symbolische Konstante erforderlich.

Also, was denkt ihr? Ist das übertrieben, oder sollte ich mich einfach an die Konvention halten und ein Symbol verwenden?

Schokoladenteufel
quelle
46
CHESS_CONST ist viel schlimmer als nur die Zahl 8 zu verwenden, aber eine Konstante mit einem beschreibenden Namen wäre eine Verbesserung. Sie sagen, jeder sollte wissen, wofür 8 im Code steht, aber das stimmt nicht. Ein ganzzahliges Literal ohne Kontext kann eine beliebige Anzahl von Dingen bedeuten, wie beispielsweise eine Anzahl von Zügen, eine Anzahl von Teilen auf dem Brett und so weiter. Ein beschreibender Name für die Konstante würde die Intention klar und damit den Code verständlicher machen.
JacquesB
43
Persönlich finde ich die Namen iund jdie Schleifenvariablen viel irritierender als die magische Zahl . Ich kann für mein ganzes Leben nicht herausfinden, welcher eine Rang und welcher eine Akte darstellen soll. Ränge reichen von 1..8 und Dateien reichen von a..h, aber in Ihrem Fall reichen beide iund j0..7, so dass mir das nicht hilft zu sehen, welche welche ist. Gibt es eine internationale Briefknappheit, von der ich nichts weiß, oder was ist falsch daran, sie in rankund fileumzubenennen?
Jörg W Mittag
4
Spielen Sie eine Sekunde lang den Anwalt des Teufels. Stellen Sie sich vor, Sie lesen jemandes Schachcode, in dem er eine magische Zahl 8 verwendet hat. Können Sie annehmen, Sie wissen, wofür er verwendet wird? Wie können Sie 100% sicher sein? Gibt es eine Möglichkeit, dass es etwas anderes bedeuten könnte? Wäre es nicht ein bisschen schöner gewesen, wenn Sie nicht einmal eine Vermutung anstellen müssten? Wie viel Zeit könnten Sie damit verbringen, den Code zu durchsuchen, um herauszufinden, ob Ihre Annahme richtig ist? Würden Sie weniger Zeit damit verbringen, den Code selbst zu dokumentieren, wenn Sie stattdessen einen aussagekräftigen, aufschlussreichen Namen verwendet hätten?
Ben Cottrell
16
@JacquesB: In der Tat. Es gibt 8 Ränge, 8 Akten, 8 Bauern. Einige Schach-Engines verwenden ein Punktesystem, bei dem bestimmten Figuren, bestimmten Feldern und bestimmten Zügen Punkte zugeordnet sind und die Engine den Zug mit der höchsten Punktzahl auswählt. 8 könnte eine solche Punktzahl sein. 8 könnte eine Standardsuchtiefe sein. Es könnte so ziemlich alles sein.
Jörg W Mittag
9
Ich würde in Betracht ziehen, eine foreach-Schleife zu verwenden, z foreach(var rank in Ranks). Ich würde auch in Betracht ziehen, beide Schleifen zu einer zusammenzuführen, wobei jedes Element ein Tupel (rank, file) ist.
CodesInChaos

Antworten:

101

IMHO hat Ihr Freund Recht, wenn er einen symbolischen Namen verwendet, obwohl ich denke, dass der Name definitiv aussagekräftiger sein sollte (wie BOARD_WIDTHanstelle von CHESS_CONST).

Auch wenn sich die Zahl während der gesamten Laufzeit des Programms nicht ändert, kann es in Ihrem Programm andere Stellen geben, an denen die Zahl 8 eine andere Bedeutung hat. Das Ersetzen von "8" durch " BOARD_WIDTHwo immer die Kartenbreite gemeint ist" und das Verwenden eines anderen symbolischen Namens, wenn etwas anderes gemeint ist, macht diese unterschiedlichen Bedeutungen explizit, offensichtlich und Ihr Gesamtprogramm lesbarer und wartbarer. Es ermöglicht Ihnen auch eine globale Suche über Ihr Programm (oder eine Rückwärtssymbolsuche, falls Ihre Umgebung dies vorsieht), falls Sie schnell alle Stellen im Code identifizieren müssen, die von der Kartenbreite abhängen.

Siehe auch diesen früheren SE.SE-Beitrag für eine Diskussion, wie man Namen für Zahlen auswählt (oder nicht).

Als Randnotiz, da es hier in den Kommentaren besprochen wurde : Wenn es im Code Ihres realen Programms darauf ankommt, ob sich die Variable iauf Zeilen und jSpalten der Karte bezieht oder umgekehrt, dann ist es empfehlenswert, Variablennamen auszuwählen, die Machen Sie die Unterscheidung klar, wie rowund col. Der Vorteil solcher Namen ist, dass sie falschen Code falsch aussehen lassen.

Doc Brown
quelle
22
Ich möchte hinzufügen, dass, wenn das OP eine wirklich großartige Schachengine geschrieben hat und diese dann um 3D-Schach oder andere Varianten erweitern möchte, es eine Herausforderung sein wird, welche 8s geändert werden müssen.
Steve Barnes
32
@SteveBarnes: Ich versuche, solche Argumente zu vermeiden, da dies zu leicht zu einem umgekehrten Argument wie "Ich denke, es ist äußerst
Doc Brown
4
@IllusiveBrian Das ist aus zwei Gründen ein klassisches "Strohmann" -Argument: (1) Nur eine Konstante zu definieren, bedeutet nicht, dass der Programmierer immer noch nicht "8" eingeben kann (ob aus Versehen, Design oder Böswilligkeit!) Und (2). Nur weil es eine Konstante BOARD_WIDTH = 8 gibt, ist BOARD_WIDTH nicht die richtige Konstante für eine bestimmte Stelle im Programm.
Alephzero
3
@Blrfl ... oder führt zu einer Überarbeitung des Codes. Es liegt in der Verantwortung des Entwicklers, zu erkennen, was sich in Zukunft ändern kann, und entsprechend zu kodieren. Coding-ähnliche Anforderungen werden sich nicht ändern, aber das andere Extrem ist nicht besser.
Malcolm
4
@corsiKa Wenn die Schleifenvariablen physischen Achsen entsprechen, sollten sie den Namen x, y oder row, col oder, für Schach, file, rank haben.
user949300
11

Ok, hier sind ein paar Kommentare, die ich habe:

Sich von magischen Zahlen zu befreien, ist eine großartige Idee. Es gibt ein Konzept namens DRY, das häufig falsch dargestellt wird, aber die Idee ist, dass Sie das Wissen über die Konzepte in Ihrem Projekt nicht duplizieren. Wenn Sie also eine Klasse mit dem Namen ChessBoard haben, können Sie eine Konstante mit dem Namen BOARD_SIZE oder ChessBoard.SIZE daran anhängen. Auf diese Weise gibt es eine einzige Quelle für diese Informationen. Dies trägt auch zur späteren Lesbarkeit bei:

for (int i = 0; i < ChessBoard.SIZE; i++){
  for (int j = 0; j < ChessBoard.SIZE; j++){

Auch wenn sich die Anzahl nie ändert, ist Ihr Programm wohl besser. Jede Person, die es liest, weiß mehr darüber, was der Code tut.

Ein schlechter Name ist schlimmer als kein Name, aber das bedeutet nicht, dass etwas nicht benannt werden sollte. Ändern Sie einfach den Namen. Werfen Sie das Baby nicht mit dem Badewasser weg. : p Der Name kann beschreibend sein, solange Sie gut verstehen, was er beschreibt. Dann kann dieses Konzept für mehrere verschiedene Dinge verwendet werden.

entblößt
quelle
8
dies scheint nur wiederholen Sie die Punkte bereits gemacht und erklärt in Top-Antwort
gnat
-7

Was Sie wirklich wollen, ist, Ad-nauseum- Verweise auf Konstanten zu entfernen , egal ob sie benannt oder nackt sind:

for_each_chess_square (row, col) {
  /*...*/
}

Wenn Sie die Konstante tatsächlich vermehren wollen, indem Sie solche Schleifen und so weiter wiederholen, ist es am besten, sich daran zu halten 8.

8ist selbstbeschreibend; Es ist kein Makro, das für etwas anderes steht.

Sie werden es nicht (TM) in ein 9x9-Schachprogramm verwandeln, und wenn Sie es jemals tun, wird die Verbreitung von 8 nicht die Hauptschwierigkeit sein.

Wir können eine 150.000-Zeilen-Code-Basis nach dem Token 8 durchsuchen und in Sekunden festlegen, welche Vorkommen was bedeuten.

Viel wichtiger ist es, den Code so zu modularisieren, dass das Schachwissen an möglichst wenigen Stellen konzentriert wird. Es ist besser, ein, zwei, vielleicht drei schachspezifische Module zu haben, in denen eine wörtliche 8 vorkommt, als siebenunddreißig Module mit schachspezifischer Verantwortung, die sich auf 8 durch einen symbolischen Namen beziehen.

Wenn diese 8-Konstante zu einer Spannungsquelle in Ihrem Programm wird, können Sie sie zu diesem Zeitpunkt problemlos beheben. Beheben Sie echte Probleme, die gerade auftreten. Wenn Sie nicht das Gefühl haben, von dieser bestimmten 8 behindert zu werden, gehen Sie mit diesem Instinkt um.

Angenommen, Sie möchten in Zukunft alternative Platinenabmessungen unterstützen. In diesem Fall müssen diese Schleifen ändern, ob sie eine benannte Konstante oder verwenden 8, da die Dimensionen durch einen Ausdruck wie board.widthund abgerufen werden board.height. Wenn Sie BOARD_SIZEstattdessen haben 8, werden diese Orte leichter zu finden sein. Das ist also weniger Aufwand. Sie müssen jedoch nicht um den Aufwand zu ersetzen , vergessen 8mit BOARD_SIZEan erster Stelle. Der Gesamtaufwand ist nicht geringer. Machen ein Pass über den Code zu ändern 8zu BOARD_SIZE, und dann auf eine andere alternative Dimensionen zu unterstützen, ist nicht billiger als gerade aus gehen 8auf alternative Dimension zu unterstützen.

Wir können dies auch anhand einer rein kalten, objektiven Risiko-Nutzen-Analyse betrachten. Das Programm enthält jetzt nackte Konstanten. Wenn diese durch eine Konstante ersetzt werden, gibt es keinen Nutzen; Das Programm ist identisch. Bei jeder Änderung besteht ein Risiko ungleich Null. In diesem Fall ist es klein. Dennoch sollte kein Risiko ohne Nutzen eingegangen werden. Um die Änderung angesichts dieser Überlegungen zu "verkaufen", müssen wir einen Nutzen annehmen: einen zukünftigen Nutzen, der bei einem anderen Programm helfen wird: eine zukünftige Version des Programms, die es derzeit nicht gibt. Wenn ein solches Programm geplant ist, sind diese Hypothese und die damit verbundenen Überlegungen zutreffend und sollten ernst genommen werden.

Wenn Sie beispielsweise noch Tage Zeit haben, um weiteren Code hinzuzufügen, der diese Konstanten weiter vermehrt, möchten Sie sie möglicherweise beseitigen. Wenn diese Instanzen der Konstanten ungefähr alle Instanzen sind, die jemals existieren werden, warum dann?

Wenn Sie jemals an kommerzieller Software arbeiten, gelten auch die ROI-Argumente. Wenn sich ein Programm nicht verkauft und das Ändern einiger fest codierter Zahlen in Konstanten den Umsatz nicht verbessert, werden Sie nicht für den Aufwand entschädigt. Die Änderung hat keine Rendite auf die Investition von Zeit. ROI-Argumente gehen über das Geld hinaus. Sie haben ein Programm geschrieben, Zeit und Mühe investiert und etwas daraus gemacht: Das ist Ihre Rendite, Ihr "R". Wenn Sie diese Änderung alleine vornehmen, erhalten Sie mit allen Mitteln mehr von diesem "R", was auch immer es ist. Wenn Sie einen Plan für die weitere Entwicklung haben und diese Änderung Ihr "R" verbessert, dito. Wenn die Änderung kein unmittelbares oder vorhersehbares "R" für Sie hat, vergessen Sie es.

Kaz
quelle
13
8ist NICHT so selbstbeschreibend, es ist eine Zahl, die alles bedeuten könnte. Und vielleicht wollen sie ein 9x9 Schachspiel machen, warum nicht? Wenn Sie über 150.000 Codezeilen verfügen, können Sie sich auf keinen Fall merken, was die einzelnen 8Codezeilen bedeuten, und selbst wenn Sie dies könnten, warum sollten Sie das tun ? Das ist nur eine Menge zusätzlicher Aufwand. Was die Modularisierung angeht, schadet das Ersetzen 8durch etwas NUM_RANKSnicht der Modularität. Tatsächlich hilft es, weil es das Basteln des Codes erleichtert.
Jerfov2
4
@ Kaz das mache ich auch. Und dann füge ich bedeutende seltsame Fehler hinzu, weil eine Handvoll Nutzungen nicht das waren, was ich dachte, weil es schwierig ist, einer großen Anzahl von Ersetzungen so viel Aufmerksamkeit zu schenken und auch jedes Mal richtig zu sein. Aus diesem Grund vermeide ich es, überhaupt in dieses Szenario einzusteigen. Daher kann ich Ratschläge, die es dulden, darauf einzugehen, nicht unterstützen.
Doppelgreener
1
"Sie dürfen jedoch nicht den Aufwand für das Ersetzen 8durch vergessen BOARD_SIZE" - Die Zeit, die Sie damit verbracht hatten, Ihren Code zu überprüfen, um herauszufinden, was jeder 8in den nächsten Monaten in Ihrem Programm tut, würde höchstwahrscheinlich die Zeit überschreiten, die für das Ersetzen 8durch aufgewendet wurde eine sinnvolle Konstante auf Modulebene.
Christian Dean
1
@doppelganger Das Szenario ist schon da. Ich sage nicht, nimm ein Schachprogramm, das Konstanten verwendet, und ersetze sie durch 8. Ich sage auch nicht, "plane, keine Konstanten in einem noch nicht geschriebenen Schachprogramm zu verwenden"
Kaz
1
@Kaz Warum solltest du den gesamten Code um die 8 herum lesen müssen und manchmal den Kontext falsch verstehen, wenn du nur zusätzliche 3 Sekunden damit verbringen könntest, einen aussagekräftigen Namen einzugeben? Um den tatsächlichen Wert der Konstanten zu kennen, ist es viel einfacher, den genauen Wert für eine Variable zu ermitteln, als rückwärts zu berechnen, was eine magische Ganzzahl im Code bedeutet
Jerfov2,