Wat is een magisch nummer, en waarom is het slecht?

Wat is een magisch getal?

Waarom zou het moeten worden vermeden?

Zijn er gevallen waarin het geschikt is?


1, Autoriteit 100%

Een magisch getal is een direct gebruik van een cijfer in de code.

Bijvoorbeeld, als u (in Java) hebt:

public class Foo {
    public void setPassword(String password) {
         // don't do this
         if (password.length() > 7) {
              throw new InvalidArgumentException("password");
         }
    }
}

Hierdoor moet worden gedefacteerd naar:

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");
         }
    }
}

Het verbetert de leesbaarheid van de code en het is gemakkelijker te onderhouden. Stel je voor dat ik de grootte van het wachtwoordveld in de GUI instel. Als ik een magisch getal gebruik, moet ik in twee codelocaties veranderen. Als ik er een vergeet, zal dit leiden tot inconsistenties.

De JDK is vol met voorbeelden zoals in Integer, Characteren Mathklassen.

PS: statische analysetools zoals Findbugs en PMD detecteert het gebruik van magische nummers in uw code en suggereert de refactoring.


2, Autoriteit 25%

Een magisch getal is een hard-gecodeerde waarde die in een later stadium kan veranderen, maar dat kan daarom moeilijk zijn om bij te werken.

Stel dat u bijvoorbeeld een pagina heeft waarop de laatste 50 bestellingen worden weergegeven op een overzichtspagina ‘Uw bestellingen’. 50 is hier het magische getal, omdat het niet door standaard of conventie is ingesteld, het is een getal dat je hebt verzonnen om redenen die in de specificatie worden beschreven.

Wat je nu doet, is dat je de 50 op verschillende plaatsen hebt – je SQL-script (SELECT TOP 50 * FROM orders), je website (je laatste 50 bestellingen), je bestellingslogin (for (i = 0; i < 50; i++)) en mogelijk vele andere plaatsen.

Wat gebeurt er als iemand besluit 50 in 25 te veranderen? of 75? of 153? Je moet nu de 50 op alle plaatsen vervangen en de kans is groot dat je het mist. Zoeken/vervangen werkt mogelijk niet, omdat 50 voor andere dingen kan worden gebruikt, en het blindelings vervangen van 50 door 25 kan andere slechte bijwerkingen hebben (bijv. uw Session.Timeout = 50-aanroep, die ook is ingesteld tot 25 en gebruikers beginnen te frequente time-outs te melden).

Ook kan de code moeilijk te begrijpen zijn, bijv. “if a < 50 then bla” – als u dat in het midden van een gecompliceerde functie tegenkomt, kunnen andere ontwikkelaars die niet bekend zijn met de code kan zich afvragen “WTF is 50???”

Daarom is het het beste om zulke dubbelzinnige en willekeurige getallen op precies 1 plaats te hebben – “const int NumOrdersToDisplay = 50“, omdat dat de code leesbaarder maakt (“if a < NumOrdersToDisplay“, betekent dit ook dat u het maar op 1 goed gedefinieerde plaats hoeft te wijzigen.

Plaatsen waar Magic Numbers geschikt zijn, is alles wat is gedefinieerd door een standaard, dwz SmtpClient.DefaultPort = 25of TCPPacketSize = whatever(niet zeker of dat gestandaardiseerd is) . Ook kan alles wat alleen binnen 1 functie is gedefinieerd acceptabel zijn, maar dat hangt af van de context.


Antwoord 3, autoriteit 6%

Heb je het Wikipedia-artikel voor magisch getal bekeken?

Het gaat dieper in op alle manieren waarop de magische getalverwijzing wordt gemaakt. Hier is een citaat over het magische getal als een slechte programmeerpraktijk

De term magisch getal verwijst ook naar de slechte programmeerpraktijk van het gebruik van nummers rechtstreeks in de broncode zonder uitleg. In de meeste gevallen maakt dit programma’s moeilijker te lezen, te begrijpen en te onderhouden. Hoewel de meeste handleidingen een uitzondering maken voor de getallen nul en één, is het een goed idee om alle andere getallen in code te definiëren als benoemde constanten.


Antwoord 4, autoriteit 4%

Magisch getal vs. Symbolische constante: wanneer vervangen?

Magie: onbekende semantiek

Symbolische constante -> Biedt zowel correcte semantische als correcte context voor gebruik

Semantisch: de betekenis of het doel van iets.

“Maak een constante, noem deze naar de betekenis en vervang het getal ervoor.” — Martin Fowler

Ten eerste zijn magische getallen niet zomaar getallen. Elke basiswaarde kan “magisch” zijn. Basiswaarden zijn manifeste entiteiten zoals gehele getallen, reals, doubles, floats, datums, strings, booleans, karakters, enzovoort. Het probleem is niet het gegevenstype, maar het “magische” aspect van de waarde zoals deze in onze codetekst wordt weergegeven.

Wat bedoelen we met ‘magie’? Om precies te zijn: met “magie” willen we verwijzen naar de semantiek (betekenis of doel) van de waarde in de context van onze code; dat het onbekend, onkenbaar, onduidelijk of verwarrend is. Dit is het begrip “magie”. Een basiswaarde is geen magie wanneer de semantische betekenis of het doel van-er-zijn snel en gemakkelijk bekend, duidelijk en begrepen (niet verwarrend) is vanuit de omringende context zonder speciale hulpwoorden (bijv. symbolische constante).

Daarom identificeren we magische getallen door het vermogen van een codelezer te meten om de betekenis en het doel van een basiswaarde uit de omringende context te kennen, duidelijk te zijn en te begrijpen. Hoe minder bekend, minder duidelijk en verwarder de lezer is, hoe “magischer” de basiswaarde is.

Nuttige definities

  • verwarren: veroorzaken dat (iemand) verbijsterd of verbijsterd raakt.
  • verbijsterd: zorg ervoor dat (iemand) perplex en verward raakt.
  • perplex: volledig verbijsterd; erg verbaasd.
  • verbijsterd: totaal verbijsterd of verbijsterd.
  • verbijsterd: niet in staat om te begrijpen; perplex.
  • begrijpen: de bedoelde betekenis van (woorden, een taal of spreker) waarnemen.
  • betekenis: wat wordt bedoeld met een woord, tekst, concept of handeling.
  • bedoelde: bedoeld om over te brengen, aan te geven of te verwijzen naar (een bepaald ding of begrip); betekenen.
  • betekenen: een indicatie zijn van.
  • indicatie: een teken of stukje informatie dat iets aangeeft.
  • aangeven: wijzen op; laten zien.
  • teken: een object, kwaliteit of gebeurtenis waarvan de aanwezigheid of het voorkomen de waarschijnlijke aanwezigheid of het voorkomen van iets anders aangeeft.

Basis

We hebben twee scenario’s voor onze magische basiswaarden. Alleen de tweede is van primair belang voor programmeurs en code:

  1. Een enkele basiswaarde (bijv. getal) waarvan de betekenis onbekend, onkenbaar, onduidelijk of verwarrend is.
  2. Een basiswaarde (bijv. getal) in context, maar de betekenis ervan blijft onbekend, onkenbaar, onduidelijk of verwarrend.

Een overkoepelende afhankelijkheid van “magie” is hoe de enige basiswaarde (bijv. getal) geen algemeen bekende semantiek heeft (zoals Pi), maar een lokaal bekende semantiek heeft (bijv. uw programma), wat niet helemaal duidelijk is uit de context of kan worden misbruikt in goede of slechte context(en).

De semantiek van de meeste programmeertalen stelt ons niet in staat om alleen basiswaarden te gebruiken, behalve (misschien) als gegevens (d.w.z. tabellen met gegevens). Wanneer we ‘magische getallen’ tegenkomen, doen we dat meestal in een context. Daarom is het antwoord op

“Moet ik dit magische getal vervangen door een symbolische constante?”

is:

“Hoe snel kun je de semantische betekenis van de . beoordelen en begrijpen?
nummer (het doel om daar te zijn) in zijn context?”

Een beetje magie, maar niet helemaal

Met deze gedachte in het achterhoofd, kunnen we snel zien hoe een getal als Pi (3.14159) geen ‘magisch getal’ is wanneer het in de juiste context wordt geplaatst (bijvoorbeeld 2 x 3.14159 x straal of 2*Pi*r). Hier is het getal 3.14159 mentaal herkende Pi zonder de symbolische constante identifier.

Toch vervangen we over het algemeen 3.14159 door een symbolische constante identifier zoals Pi vanwege de lengte en complexiteit van het getal. De aspecten van lengte en complexiteit van Pi (in combinatie met de behoefte aan nauwkeurigheid) betekent meestal dat de symbolische identifier of constante minder foutgevoelig is. Erkenning van “Pi” als naam is gewoon een handige bonus, maar is niet de belangrijkste reden voor het hebben van de constante.

Ondertussen: Terug op de Ranch

Laten we algemene constanten zoals Pi buiten beschouwing laten, laten we ons vooral concentreren op getallen met speciale betekenissen, maar waarvan die betekenissen beperkt zijn tot het universum van ons softwaresysteem. Zo’n getal kan “2” zijn (als een basisgetalwaarde).

Als ik het cijfer 2 alleen gebruik, zou mijn eerste vraag kunnen zijn: wat betekent “2”? De betekenis van “2” is op zichzelf onbekend en onkenbaar zonder context, waardoor het gebruik ervan onduidelijk en verwarrend is. Hoewel alleen “2” in onze software niet zal gebeuren vanwege taalsemantiek, willen we wel zien dat “2” op zichzelf geen speciale semantiek of duidelijk doel heeft om alleen te zijn.

Laten we onze enige “2” in een context plaatsen van: padding := 2, waarbij de context een “GUI-container” is. In deze context biedt de betekenis van 2 (als pixels of andere grafische eenheid) ons een snelle gok van de semantiek (betekenis en doel). We kunnen hier stoppen en zeggen dat 2 in deze context oké is en dat we verder niets hoeven te weten. Maar misschien is dit in ons software-universum niet het hele verhaal. Er is meer aan de hand, maar “padding = 2” als context kan het niet onthullen.

Laten we verder doen alsof 2 als pixelpadding in ons programma van de “default_padding” variant is in ons hele systeem. Daarom is het schrijven van de instructie padding = 2niet goed genoeg. Het begrip “standaard” wordt niet onthuld. Pas als ik schrijf: padding = default_paddingals context en dan ergens anders: default_padding = 2realiseer ik me volledig een betere en volledigere betekenis (semantisch en doel) van 2 in onze systeem.

Het bovenstaande voorbeeld is redelijk goed omdat “2” op zichzelf alles kan zijn. Alleen wanneer we het bereik en domein van begrip beperken tot “mijn programma”, waarbij 2 de default_paddingis in de GUI UX-gedeelten van “mijn programma”, begrijpen we eindelijk “2” in zijn eigenlijke zin context. Hier is “2” een “magisch” getal, dat is weggewerkt naar een symbolische constante default_paddingbinnen de context van de GUI UX van “mijn programma” om het te laten gebruiken als default_paddingsnel begrepen in de grotere context van de omsluitende code.

Dus elke basiswaarde waarvan de betekenis (semantisch en doel) niet voldoende en snel kan worden begrepen, is een goede kandidaat voor een symbolische constante in de plaats van de basiswaarde (bijvoorbeeld magisch getal).

Verder gaan

Getallen op een schaal kunnen ook semantiek hebben. Stel bijvoorbeeld dat we een D&D-spel maken, waarbij we het idee hebben van een monster. Ons monsterobject heeft een functie genaamd life_force, wat een geheel getal is. De getallen hebben betekenissen die niet kenbaar of duidelijk zijn zonder woorden om betekenis te geven. We beginnen dus met willekeurig te zeggen:

  • full_life_force: INTEGER = 10 — Zeer levend (en ongedeerd)
  • minimum_life_force: INTEGER = 1 — Nauwelijks in leven (zeer gewond)
  • dood: INTEGER = 0 — Dood
  • ondood: INTEGER = -1 — Min ondood (bijna dood)
  • zombie: INTEGER = -10 — Max ondood (zeer ondood)

Van de bovenstaande symbolische constanten krijgen we een mentaal beeld van de levendigheid, doodsheid en ‘ondood’ (en mogelijke gevolgen of gevolgen) voor onze monsters in ons D&D-spel. Zonder deze woorden (symbolische constanten), blijven alleen de getallen over van -10 .. 10. Alleen het bereik zonder de woorden laat ons in een plaats van mogelijk grote verwarring en mogelijk met fouten in ons spel als verschillende delen van het spel afhankelijk zijn van wat dat bereik van getallen betekent voor verschillende bewerkingen zoals attack_elvesof seek_magic_healing_potion.

Daarom willen we bij het zoeken naar en het overwegen van vervanging van “magische getallen” zeer doelgerichte vragen stellen over de getallen binnen de context van onze software en zelfs hoe de getallen semantisch met elkaar omgaan.

Conclusie

Laten we eens kijken welke vragen we zouden moeten stellen:

Je hebt misschien een magisch getal als …

  1. Kan de basiswaarde een speciale betekenis of doel hebben in uw software-universum?
  2. Kan de speciale betekenis of het doel waarschijnlijk onbekend, onkenbaar, onduidelijk of verwarrend zijn, zelfs in de juiste context?
  3. Kan een juiste basiswaarde verkeerd worden gebruikt met slechte gevolgen in de verkeerde context?
  4. Kan een onjuiste basiswaarde correct worden gebruikt met slechte gevolgen in de juiste context?
  5. Heeft de basiswaarde een semantische of doelrelatie met andere basiswaarden in specifieke contexten?
  6. Kan een basiswaarde op meer dan één plaats in onze code voorkomen met verschillende semantiek in elke, waardoor onze lezer in verwarring raakt?

Bekijk stand-alone manifest constante basiswaarden in uw codetekst. Stel elke vraag langzaam en bedachtzaam over elk exemplaar van een dergelijke waarde. Overweeg de kracht van uw antwoord. Vaak is het antwoord niet zwart-wit, maar heeft het tinten van verkeerd begrepen betekenis en doel, snelheid van leren en snelheid van begrip. Er is ook behoefte om te zien hoe het verbinding maakt met de softwaremachine eromheen.

Uiteindelijk is het antwoord op vervanging het antwoord op de maat (in uw gedachten) van de sterkte of zwakte van de lezer om de verbinding te maken (bijvoorbeeld “snap it”). Hoe sneller ze de betekenis en het doel begrijpen, hoe minder “magie” je hebt.

CONCLUSIE: Vervang basiswaarden alleen door symbolische constanten als de magie groot genoeg is om moeilijk te detecteren bugs te veroorzaken die voortkomen uit verwarring.


Antwoord 5, autoriteit 3%

Een magisch getal is een reeks tekens aan het begin van een bestandsindeling of protocoluitwisseling. Dit nummer dient als een gezondheidscheck.

Voorbeeld:
Open een willekeurig GIF-bestand, u ziet helemaal aan het begin: GIF89. “GIF89” is het magische getal.

Andere programma’s kunnen de eerste paar tekens van een bestand lezen en GIF’s correct identificeren.

Het gevaar is dat willekeurige binaire gegevens dezelfde tekens kunnen bevatten. Maar het is zeer onwaarschijnlijk.

Wat betreft protocoluitwisseling, je kunt het gebruiken om snel te identificeren dat het huidige ‘bericht’ dat aan je wordt doorgegeven, beschadigd of niet geldig is.

Magische getallen zijn nog steeds nuttig.


Antwoord 6, autoriteit 2%

Bij het programmeren is een “magisch getal” een waarde die een symbolische naam zou moeten krijgen, maar die in plaats daarvan letterlijk in de code is geschoven, meestal op meer dan één plaats.

Het is slecht om dezelfde reden dat SPOT (Single Point of Truth) goed is: als je deze constante later zou willen veranderen, zou je door je code moeten jagen om elke instantie te vinden. Het is ook slecht omdat het voor andere programmeurs misschien niet duidelijk is wat dit nummer vertegenwoordigt, vandaar de “magie”.

Mensen gaan soms verder met het elimineren van magische getallen door deze constanten naar afzonderlijke bestanden te verplaatsen om als configuratie te fungeren. Dit is soms handig, maar kan ook voor meer complexiteit zorgen dan het waard is.


Antwoord 7, autoriteit 2%

Een probleem dat niet is genoemd met het gebruik van magische getallen…

Als je er heel veel hebt, is de kans redelijk goed dat je twee verschillende doeleindenhebt waarvoor je magische getallen gebruikt, waarbij de waardentoevallig zijn hetzelfde zijn.

En dan, ja hoor, je hoeft de waarde maar voor één doel te wijzigen.


Antwoord 8, autoriteit 2%

Een magisch getal kan ook een getal zijn met speciale, hardgecodeerde semantiek. Ik zag bijvoorbeeld eens een systeem waarbij record-ID’s > 0 werden normaal behandeld, 0 zelf was “nieuw record”, -1 was “dit is de root” en -99 was “dit is gemaakt in de root”. 0 en -99 zouden ervoor zorgen dat de WebService een nieuwe ID opgeeft.

Het slechte hieraan is dat je een spatie (die van getekende gehele getallen voor record-ID’s) opnieuw gebruikt voor speciale vaardigheden. Misschien wil je nooit een record maken met ID 0, of met een negatieve ID, maar zelfs als dat niet het geval is, kan iedereen die naar de code of naar de database kijkt, hierover struikelen en in het begin in de war raken. Het spreekt vanzelf dat die speciale waarden niet goed gedocumenteerd waren.

Ongetwijfeld 22, 7, -12 en 620 tellen ook als magische getallen. 😉


Antwoord 9

Ik neem aan dat dit een reactie is op mijn antwoordop uw eerdere vraag. Bij het programmeren is een magisch getal een ingebedde numerieke constante die zonder uitleg verschijnt. Als het op twee verschillende locaties voorkomt, kan dit leiden tot omstandigheden waarin het ene exemplaar wordt gewijzigd en het andere niet. Om beide redenen is het belangrijk om de numerieke constanten te isoleren en te definiëren buiten de plaatsen waar ze worden gebruikt.


Antwoord 10

Ik heb de term ‘magisch getal’ altijd anders gebruikt, als een obscure waarde die is opgeslagen in een gegevensstructuur die kan worden geverifieerd als een snelle validiteitscontrole. Gzip-bestanden bevatten bijvoorbeeld 0x1f8b08 als hun eerste drie bytes, Java-klassebestanden beginnen met 0xcafebabe, enz.

Je ziet vaak magische getallen die zijn ingesloten in bestandsindelingen, omdat bestanden nogal promiscue kunnen worden verzonden en alle metagegevens over hoe ze zijn gemaakt, kunnen verliezen. Maar magische getallen worden soms ook gebruikt voor gegevensstructuren in het geheugen, zoals ioctl()-aanroepen.

Een snelle controle van het magische getal voordat het bestand of de gegevensstructuur wordt verwerkt, stelt iemand in staat fouten vroegtijdig te signaleren, in plaats van de hele weg door mogelijk langdurige verwerking te slepen om aan te kondigen dat de invoer compleet balderdash was.


Antwoord 11

Het is vermeldenswaard dat u soms niet-configureerbare “hard-gecodeerde” nummers in uw code wilt. Er zijn een aantal beroemdewaaronder 0x5F3759DF die wordt gebruikt in het geoptimaliseerde inverse vierkant root-algoritme.

In de zeldzame gevallen dat ik dergelijke magische nummers moet gebruiken, stel ik ze in als een const in mijn code en documenteer ik waarom ze worden gebruikt, hoe ze werken en waar ze vandaan komen.


Antwoord 12

Hoe zit het met het initialiseren van een variabele aan de bovenkant van de klas met een standaardwaarde? Bijvoorbeeld:

public class SomeClass {
    private int maxRows = 15000;
    ...
    // Inside another method
    for (int i = 0; i < maxRows; i++) {
        // Do something
    }
    public void setMaxRows(int maxRows) {
        this.maxRows = maxRows;
    }
    public int getMaxRows() {
        return this.maxRows;
    }

In dit geval is 15000 een magisch getal (volgens controles). Voor mij is het instellen van een standaardwaarde in orde. Ik wil het niet doen:

private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;

maakt dat het moeilijker om te lezen? Ik heb dit nooit overwogen totdat ik checkstyles heb geïnstalleerd.


13

@ EED3SI9N: Ik zou zelfs voorstellen dat ‘1’ een magisch nummer is. : -)

Een principe dat gerelateerd is aan magische getallen is dat elk feit dat uw code deals precies één keer moet worden aangegeven. Als u magische nummers in uw code gebruikt (zoals het voorbeeld van het wachtwoordlengte dat @marcio gaf, kunt u eenvoudig het feit dat u dat feit gemakkelijk kunt dupliceren, en wanneer uw inzicht is van die veranderingen die u een onderhoudsprobleem hebt.


Antwoord 14

Een ander voordeel van het extraheren van een magisch getal als constante geeft de mogelijkheid om de bedrijfsinformatie duidelijk te documenteren.

public class Foo {
    /** 
     * Max age in year to get child rate for airline tickets
     * 
     * The value of the constant is {@value}
     */
    public static final int MAX_AGE_FOR_CHILD_RATE = 2;
    public void computeRate() {
         if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
               applyChildRate();
         }
    }
}

Other episodes