Co to jest liczba magiczna i dlaczego jest zła? [Zamknięte]

514

Co to jest magiczna liczba?

Dlaczego należy tego unikać?

Czy istnieją przypadki, w których jest to właściwe?

Adam Davis
źródło
2
Uniknąłbyś magicznych liczb, ponieważ inne osoby oglądające Twój kod mogą nie rozumieć, dlaczego robisz to, co robisz ... np. const myNum = 22; const number = myNum / 11;Teraz moje 11 może być ludźmi lub butelkami piwa lub coś takiego, zamiast tego zmieniłbym 11 na stały takich jak mieszkańcy.
JuicY_Burrito,
Używanie magicznych liczb w atrybutach jest nieuniknione, więc myślę, że jest to właściwe.
donatasj87

Odpowiedzi:

574

Magiczna liczba to bezpośrednie użycie liczby w kodzie.

Na przykład, jeśli masz (w Javie):

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

Należy to zmienić na:

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

Poprawia czytelność kodu i jest łatwiejszy w utrzymaniu. Wyobraź sobie przypadek, w którym ustawiam rozmiar pola hasła w GUI. Jeśli użyję magicznej liczby, ilekroć zmienia się maksymalny rozmiar, muszę zmienić w dwóch lokalizacjach kodu. Jeśli zapomnę o jednym, doprowadzi to do niespójności.

JDK jest pełna przykładów, jak w Integer, Characteri Mathklas.

PS: Narzędzia analizy statycznej, takie jak FindBugs i PMD, wykrywają użycie magicznych liczb w kodzie i sugerują refaktoryzację.

Marcio Aguiar
źródło
174
0 i 1 są wyjątkami od tej reguły.
Jonathan Parker,
24
@Kirill: Jeśli spodziewasz się zmiany definicji „stu procent”, to tak. Lepszym podejściem byłoby posiadanie zmiennej od tego, co jest, do tego, co reprezentuje, tj. Publiczny statyczny końcowy MAX_DOWNLOAD_PERCENTAGE = 100. Chociaż nawet to nie miałoby sensu, ponieważ „100 procent” jest bardzo dobrze zdefiniowane. Z drugiej strony, fakt, że Hasła mogą mieć maksymalnie 7 znaków, nie jest globalnie zdefiniowany i faktycznie się różni, więc jest to kandydat na zmienną.
Michael Stum
40
@Jonathan Parker, z wyjątkiem sytuacji, gdy nie są ( TRUE/ FALSE)
Brendan Long
82
To, że magiczna liczba nigdy się nie zmieni, nie oznacza, że ​​nie należy jej zastępować stałą. Mój kod jest pełen globalnych stałych, takich jak HzPerMHz i msecPerSecond. Te nigdy się nie zmienią, ale wyjaśniają znaczenie i zapewniają pewną ochronę przed literówkami.
Jeanne Pindar
43
@MarcusJ Nie możesz się bardziej mylić. Nie jest to kwestia opinii, ale ciężko zdobytego doświadczenia przez wielu programistów. Nie mogę powiedzieć, ile razy w ciągu ostatnich 40 lat programowania przekląłem poprzedniego programistę, który nie zdefiniował stałej, więc odkryłem tylko bezpośrednie użycie liczby, którą trzeba zrozumieć podczas konserwacji kodu , pochowany gdzieś w dużym kodzie, którego znaczenie można by wyjaśnić, definiując taką stałą. Każdy inny starszy programista będzie również miał wiele horrorów wzdłuż tej linii.
ToolmakerSteve
145

Magiczna liczba jest zakodowaną wartością, która może ulec zmianie na późniejszym etapie, ale może być trudna do aktualizacji.

Załóżmy na przykład, że masz stronę wyświetlającą ostatnie 50 zamówień na stronie przeglądu „Twoje zamówienia”. 50 jest tutaj Magiczną Liczbą, ponieważ nie jest ustalana przez standard lub konwencję, jest to liczba, którą wymyśliłeś z powodów opisanych w specyfikacji.

Teraz masz 50 w różnych miejscach - w twoim skrypcie SQL ( SELECT TOP 50 * FROM orders), w swojej witrynie (Twoje ostatnie 50 zamówień), w loginach zamówień ( for (i = 0; i < 50; i++)) i prawdopodobnie w wielu innych miejscach.

Co się stanie, gdy ktoś zdecyduje się zmienić 50 na 25? lub 75? lub 153? Teraz musisz wymienić 50 we wszystkich miejscach i bardzo prawdopodobne, że go przegapisz. Znajdź / Zamień może nie działać, ponieważ 50 można użyć do innych celów, a zamiana 50 na 25 na ślepo może mieć inne złe skutki uboczne (np. Twoje Session.Timeout = 50połączenie, które jest również ustawione na 25 i użytkownicy zaczynają zgłaszać zbyt częste przerwy).

Ponadto kod może być trudny do zrozumienia, tj. „ if a < 50 then bla” - jeśli napotkasz to w środku skomplikowanej funkcji, inni programiści, którzy nie znają kodu, mogą zadać sobie pytanie „WTF wynosi 50 ???”

Dlatego najlepiej jest mieć tak niejednoznaczne i dowolne liczby dokładnie w jednym miejscu - „ const int NumOrdersToDisplay = 50”, ponieważ dzięki temu kod jest bardziej czytelny („ if a < NumOrdersToDisplay”, oznacza to również, że musisz go zmienić tylko w 1 dobrze zdefiniowanym miejscu.

Miejsca, w których odpowiednie są Liczby magiczne, to wszystko, co jest zdefiniowane przez standard, tj. SmtpClient.DefaultPort = 25Lub TCPPacketSize = whatever(nie jestem pewien, czy to jest znormalizowane). Ponadto wszystko zdefiniowane tylko w ramach 1 funkcji może być dopuszczalne, ale zależy to od kontekstu.

Michael Stum
źródło
18
Nawet jeśli nie można tego zmienić, to wciąż zły pomysł, ponieważ nie jest jasne, co się dzieje.
Loren Pechtel
11
Nie zawsze jest niejasne. SmtpClient.DefaultPort = 25jest możliwie jasne er niż SmtpClient.DefaultPort = DEFAULT_SMTP_PORT.
user253751,
4
@immibis Przypuszczam, że przy założeniu, że nie ma absolutnie żadnego innego kodu, który wykorzystuje koncepcję DEFAULT_SMTP_PORT. Jeśli domyślny port SMTP dla tej aplikacji zostanie zmieniony, należy go zaktualizować w wielu miejscach, co może spowodować niespójność.
Russ Bradberry,
3
Trudniej jest również znaleźć wszystkie zastosowania - musisz wyszukać w 25całej aplikacji i upewnić się, że zmienisz tylko te wystąpienia, 25które dotyczą portu SMTP, a nie 25, które są np. Szerokością kolumny tabeli lub liczbą rekordów do wyświetlenia na stronie.
Michael Stum
2
W tym przykładzie spodziewam się, że kod będzie używał SmtpClient.DefaultPort, a nie 25. Więc musisz go zmienić tylko w jednym miejscu. A numer portu prawdopodobnie pozostanie taki sam, nie jest to losowa liczba magiczna, ale liczba przypisana przez IANA.
njsg
34

Czy spojrzałeś na wpis w Wikipedii dotyczący magicznej liczby?

Przedstawiono w nim trochę szczegółów na temat wszystkich sposobów tworzenia odniesienia do magicznej liczby. Oto cytat o magicznej liczbie jako złej praktyce programistycznej

Termin magiczna liczba odnosi się również do złej praktyki programowania polegającej na używaniu liczb bezpośrednio w kodzie źródłowym bez wyjaśnienia. W większości przypadków sprawia to, że programy są trudniejsze do odczytania, zrozumienia i utrzymania. Chociaż większość przewodników robi wyjątek dla liczb zero i jeden, dobrym pomysłem jest zdefiniowanie wszystkich innych liczb w kodzie jako nazwanych stałych.

somas1
źródło
8
Dobry przykład RTFW :)
Eva
Powiedziałbym, że odpowiedź jest daleka od pełnej.
Skeeve
25

Magic Number vs. Stała symboliczna: kiedy wymienić?

Magia: Nieznany semantyczny

Stała symboliczna -> Zapewnia poprawny kontekst semantyczny i poprawny kontekst do użycia

Semantyczny: znaczenie lub cel rzeczy.

„Utwórz stałą, nazwij ją po znaczeniu i zamień na nią liczbę”. - Martin Fowler

Po pierwsze, magiczne liczby to nie tylko liczby. Każda podstawowa wartość może być „magiczna”. Podstawowe wartości to oczywiste jednostki, takie jak liczby całkowite, liczby rzeczywiste, liczby podwójne, liczby zmiennoprzecinkowe, daty, łańcuchy, wartości logiczne, znaki i tak dalej. Problemem nie jest typ danych, ale „magiczny” aspekt wartości, jaki pojawia się w naszym tekście kodu.

Co rozumiemy przez „magię”? Mówiąc precyzyjniej: „magią” zamierzamy wskazać na semantykę (znaczenie lub cel) wartości w kontekście naszego kodu; że jest nieznane, niepoznawalne, niejasne lub mylące. To jest pojęcie „magii”. Podstawową wartością nie jest magia, gdy jej semantyczne znaczenie lub cel bycia są tam szybko i łatwo znane, jasne i zrozumiałe (nie mylące) z otaczającego kontekstu bez specjalnych słów pomocniczych (np. Stałej symbolicznej).

Dlatego identyfikujemy magiczne liczby, mierząc zdolność czytnika kodów do poznania, wyjaśnienia i zrozumienia znaczenia i celu podstawowej wartości z otaczającego kontekstu. Im mniej znany, mniej czytelny i bardziej zagmatwany czytelnik, tym bardziej „magiczna” jest podstawowa wartość.

Przydatne definicje

  • mylić: powodować (zdezorientowanie) kogoś.
  • oszołomiony: wywołać zakłopotanie i dezorientację (kogoś).
  • zakłopotany: całkowicie zaskoczony; bardzo zdziwiony.
  • zaskoczony: całkowicie oszołomiony lub zakłopotany.
  • zdziwiony: niezdolny do zrozumienia; zakłopotany.
  • Rozumiem: postrzegaj zamierzone znaczenie (słowa, język lub mówca).
  • znaczenie: co oznacza słowo, tekst, pojęcie lub działanie.
  • oznaczało: zamierzać przekazać, wskazać lub odnieść się do (konkretnej rzeczy lub pojęcia); oznaczać.
  • oznacza: bądź wskazówką.
  • wskazanie: znak lub informacja, która coś wskazuje.
  • wskazać: wskazać; pokazać.
  • znak: obiekt, jakość lub zdarzenie, którego obecność lub wystąpienie wskazuje na prawdopodobną obecność lub wystąpienie czegoś innego.

Podstawy

Mamy dwa scenariusze dla naszych magicznych podstawowych wartości. Tylko druga kwestia ma podstawowe znaczenie dla programistów i kodu:

  1. Pojedyncza podstawowa wartość (np. Liczba), od której jej znaczenie jest nieznane, nieznane, niejasne lub mylące.
  2. Podstawowa wartość (np. Liczba) w kontekście, ale jej znaczenie pozostaje nieznane, nieznane, niejasne lub mylące.

Nadrzędną zależnością „magii” jest to, że samotna podstawowa wartość (np. Liczba) nie ma powszechnie znanego semantycznego (jak Pi), ale ma lokalnie znaną semantyczną (np. Twój program), która nie jest całkowicie jasna z kontekstu lub może być nadużywana w dobrym lub złym kontekście (kontekstach).

Semantyka większości języków programowania nie pozwoli nam używać samotnych podstawowych wartości, z wyjątkiem (być może) jako danych (tj. Tabel danych). Kiedy napotykamy „magiczne liczby”, zwykle robimy to w kontekście. Dlatego odpowiedź na

„Czy zastąpię tę magiczną liczbę stałą symboliczną?”

jest:

„Jak szybko potrafisz ocenić i zrozumieć semantyczne znaczenie liczby (jej cel bycia tam) w jej kontekście?”

Rodzaj magii, ale niezupełnie

Mając to na uwadze, możemy szybko zobaczyć, jak liczba taka jak Pi (3.14159) nie jest „liczbą magiczną”, gdy jest umieszczona w odpowiednim kontekście (np. 2 x 3,14159 x promień lub 2 * Pi * r). Tutaj liczba 3.14159 jest mentalnie rozpoznawana Pi bez symbolicznego stałego identyfikatora.

Nadal jednak zastępujemy 3.14159 symbolicznym stałym identyfikatorem, takim jak Pi, ze względu na długość i złożoność liczby. Aspekty długości i złożoności Pi (w połączeniu z potrzebą dokładności) zwykle oznaczają, że symboliczny identyfikator lub stała jest mniej podatna na błędy. Rozpoznanie „Pi” jako nazwy jest po prostu wygodnym bonusem, ale nie jest głównym powodem posiadania stałej.

Tymczasem z powrotem na ranczo

Pomijając zwykłe stałe, takie jak Pi, skupmy się przede wszystkim na liczbach o specjalnych znaczeniach, które jednak są ograniczone do wszechświata naszego systemu oprogramowania. Taką liczbą może być „2” (jako podstawowa wartość całkowita).

Jeśli użyję samej cyfry 2, moje pierwsze pytanie może brzmieć: co oznacza „2”? Znaczenie samego „2” jest nieznane i niepoznawalne bez kontekstu, przez co jego użycie jest niejasne i mylące. Chociaż posiadanie po prostu „2” w naszym oprogramowaniu nie nastąpi z powodu semantyki języka, chcemy, aby „2” samo w sobie nie miało żadnej szczególnej semantyki ani oczywistego celu.

Umieśćmy nasze samotne „2” w kontekście: padding := 2 gdzie kontekstem jest „Kontener GUI”. W tym kontekście znaczenie 2 (jako pikseli lub innej jednostki graficznej) pozwala szybko odgadnąć jego semantykę (znaczenie i cel). Możemy zatrzymać się tutaj i powiedzieć, że 2 jest w tym kontekście w porządku i nie musimy nic więcej wiedzieć. Być może jednak w naszym oprogramowaniu nie jest to cała historia. Jest w tym coś więcej, ale „padding = 2” jako kontekst nie może tego ujawnić.

Udawajmy dalej, że 2 jako wypełnianie pikseli w naszym programie jest odmianą „default_padding” w całym naszym systemie. Dlatego napisanie instrukcji padding = 2nie jest wystarczająco dobre. Pojęcie „domyślnie” nie zostało ujawnione. Dopiero gdy piszę: padding = default_paddingjako kontekst, a potem gdzie indziej: w default_padding = 2pełni zdaję sobie sprawę z lepszego i pełniejszego znaczenia (semantycznego i celu) 2 w naszym systemie.

Powyższy przykład jest całkiem dobry, ponieważ „2” samo w sobie może być czymkolwiek. Dopiero gdy ograniczymy zasięg i dziedzinę zrozumienia do „mojego programu”, gdzie 2 to części default_paddingGUI UX „mojego programu”, w końcu rozumiemy „2” w jego odpowiednim kontekście. W tym przypadku „2” jest liczbą „magiczną”, która jest uwzględniana jako stała symboliczna default_paddingw kontekście GUI UX „mojego programu”, aby można było z niej korzystać tak default_paddingszybko, jak rozumiane w szerszym kontekście załączonego kodu.

Zatem każda podstawowa wartość, której znaczenia (semantycznego i celu) nie można dostatecznie i szybko zrozumieć, jest dobrym kandydatem na symboliczną stałą zamiast wartości podstawowej (np. Liczba magiczna).

Idąc dalej

Liczby w skali również mogą mieć semantykę. Udawajmy na przykład, że tworzymy grę D&D, w której mamy pojęcie potwora. Nasz obiekt potwora ma funkcję o nazwie life_force, która jest liczbą całkowitą. Liczby mają znaczenia, które nie są poznawalne ani jasne bez słów, które dostarczają znaczenia. Dlatego zaczynamy od arbitralnego powiedzenia:

  • full_life_force: INTEGER = 10 - Bardzo żywy (i nienaruszony)
  • minimum_life_force: INTEGER = 1 - Ledwo żywy (bardzo zraniony)
  • martwy: INTEGER = 0 - martwy
  • nieumarli: INTEGER = -1 - Min nieumarli (prawie martwi)
  • zombie: INTEGER = -10 - Max nieumarły (bardzo nieumarły)

Na podstawie powyższych symbolicznych stałych zaczynamy uzyskiwać mentalny obraz żywotności, śmierci i „nieumarłości” (i możliwych konsekwencji lub konsekwencji) dla naszych potworów w naszej grze D&D. Bez tych słów (stałych symbolicznych) pozostałyby nam tylko liczby od -10 .. 10. Sam zakres bez słów pozostawia nas w miejscu potencjalnie wielkiego zamieszania i potencjalnie z błędami w naszej grze, jeśli różne części gry są zależne od tego, co ten zakres liczb oznacza dla różnych operacji, takich jak attack_elveslub seek_magic_healing_potion.

Dlatego szukając i rozważając zamianę „magicznych liczb”, chcemy zadać bardzo wypełnione celami pytania dotyczące liczb w kontekście naszego oprogramowania, a nawet tego, jak liczby oddziałują semantycznie ze sobą.

Wniosek

Zobaczmy, jakie pytania powinniśmy zadać:

Możesz mieć magiczną liczbę, jeśli ...

  1. Czy podstawowa wartość może mieć specjalne znaczenie lub cel we wszechświecie oprogramowania?
  2. Czy specjalne znaczenie lub cel mogą być nieznane, niepoznawalne, niejasne lub mylące, nawet we właściwym kontekście?
  3. Czy można niewłaściwie zastosować odpowiednią wartość podstawową ze złymi konsekwencjami w niewłaściwym kontekście?
  4. Czy niewłaściwe wartości podstawowe mogą być właściwie stosowane ze złymi konsekwencjami we właściwym kontekście?
  5. Czy wartość podstawowa ma związek semantyczny lub celowy z innymi podstawowymi wartościami w określonych kontekstach?
  6. Czy podstawowa wartość może istnieć w więcej niż jednym miejscu w naszym kodzie z różną semantyką w każdym, powodując w ten sposób zamieszanie w naszym czytelniku?

Zbadaj autonomiczne manifest stałe podstawowe wartości w tekście kodu. Zadaj powoli każde pytanie o każde wystąpienie takiej wartości. Zastanów się nad siłą swojej odpowiedzi. Wiele razy odpowiedź nie jest czarno-biała, ale ma odcienie niezrozumiałego znaczenia i celu, szybkość nauki i szybkość zrozumienia. Konieczne jest również sprawdzenie, jak łączy się z otaczającą go maszyną programową.

Ostatecznie odpowiedzią na zamianę jest odpowiedź na miarę (w twoim umyśle) siły lub słabości czytelnika do nawiązania połączenia (np. „Pobierz”). Im szybciej rozumieją znaczenie i cel, tym mniej masz „magii”.

WNIOSEK: Zastąp podstawowe wartości stałymi symbolicznymi tylko wtedy, gdy magia jest wystarczająco duża, aby spowodować trudne do wykrycia błędy wynikające z zamieszania.

Larry
źródło
1
Dzięki. FWiW narzędzia do analizy statycznej, które instalują moi koledzy, narzekają na magiczne liczby - ale jak to narzędzie ma rozumieć semantykę? W rezultacie WSZYSTKIE podstawowe wartości są zastępowane stałymi symbolicznymi. Ponieważ zgadzam się z twoją konkluzją, uważam to za mniej niż idealne.
Chomeh
17

Magiczna liczba to sekwencja znaków na początku formatu pliku lub wymiany protokołu. Ten numer służy jako kontrola poczytalności.

Przykład: Otwórz dowolny plik GIF, zobaczysz na samym początku: GIF89. „GIF89” to magiczna liczba.

Inne programy mogą odczytać kilka pierwszych znaków pliku i poprawnie zidentyfikować pliki GIF.

Niebezpieczeństwo polega na tym, że losowe dane binarne mogą zawierać te same znaki. Ale to bardzo mało prawdopodobne.

Jeśli chodzi o wymianę protokołu, możesz go użyć, aby szybko stwierdzić, że bieżący „komunikat”, który jest do Ciebie przesyłany, jest uszkodzony lub nieprawidłowy.

Liczby magiczne są nadal przydatne.

Brian R. Bondy
źródło
13
Nie sądzę, żeby to była magiczna liczba, o której mówił
Marcio Aguiar,
4
Może powinieneś usunąć dodane przez ciebie tagi „format pliku” i „sieć”, ponieważ wyraźnie nie mówi o tego rodzaju magicznych liczbach.
Landon
9
Nadal bardzo przydatne jest wiedzieć, że magiczne liczby mogą odnosić się nie tylko do problemu z kodem. -Adam
Adam Davis
3
Jeśli temat brzmiał: „Co to jest magiczna liczba pod względem kodu źródłowego”, tagi nie powinny tam być. Ale nie sprecyzował tego. Więc posiadanie moich dodatkowych informacji jest dobre. Myślę, że Kyle, Landon i Marcio się mylą.
Brian R. Bondy,
4
Nie było też sposobu, aby ustalić, którego szukał. Odkąd byłem pierwszym postem, nie mogłem zgadnąć, którego szukał.
Brian R. Bondy,
12

W programowaniu „magiczna liczba” to wartość, której należy nadać nazwę symboliczną, ale zamiast tego została wstawiona do kodu jako literał, zwykle w więcej niż jednym miejscu.

Jest to złe z tego samego powodu, dla którego SPOT (Single Point of Truth) jest dobry: jeśli chcesz zmienić tę stałą później, musisz przeszukać swój kod, aby znaleźć każdą instancję. Jest to również złe, ponieważ innym programistom może nie być jasne, co oznacza ta liczba, stąd „magia”.

Ludzie czasami jeszcze bardziej eliminują magiczne liczby, przenosząc te stałe do osobnych plików, aby działały jako konfiguracja. Czasami jest to pomocne, ale może również powodować większą złożoność, niż jest to warte.

Nick Retallack
źródło
Czy możesz sprecyzować, dlaczego wyeliminowanie liczb maginowych NIE zawsze jest dobre?
Marcio Aguiar,
W formułach matematycznych, takich jak e ^ pi + 1 = 0
Jared Updike
5
Marcio: Kiedy robisz takie rzeczy jak „const int EIGHT = 8;” a potem wymagania się zmieniają i kończy się na „const int EIGHT = 9;”
jmucchiello
6
Przepraszam, ale to po prostu przykład złego nazewnictwa lub podstawowe użycie stałej.
Kzqai,
1
@MarcioAguiar: Na niektórych platformach wyrażenie takie (foo[i]+foo[i+1]+foo[i+2]+1)/3może być oceniane znacznie szybciej niż pętla. Jeśli ktoś zastąpi 3kod bez przepisywania kodu jako pętli, ktoś, kto widział ITEMS_TO_AVERAGEzdefiniowany jako, 3może pomyśleć, że może go zmienić 5i mieć średni kod więcej elementów. Natomiast ktoś, kto spojrzy na wyrażenie dosłowne 3, zda sobie sprawę, że 3reprezentuje liczbę sumowanych elementów.
supercat
10

Magiczna liczba może być również liczbą ze specjalną, zakodowaną semantyką. Na przykład raz widziałem system, w którym identyfikatory rekordów> 0 były traktowane normalnie, samo 0 było „nowym rekordem”, -1 to „to jest root”, a -99 to „to zostało utworzone w rootie”. 0 i -99 spowodowałyby, że WebService poda nowy identyfikator.

Złe w tym jest to, że ponownie używasz spacji (liczby podpisanych liczb całkowitych dla identyfikatorów rekordów) na specjalne zdolności. Być może nigdy nie będziesz chciał utworzyć rekordu z identyfikatorem 0 lub z identyfikatorem ujemnym, ale nawet jeśli nie, każda osoba, która patrzy na kod lub bazę danych, może się na to natknąć i na początku będzie się mylić. Oczywiste jest, że te specjalne wartości nie były dobrze udokumentowane.

Prawdopodobnie 22, 7, -12 i 620 również liczą się jako liczby magiczne. ;-)

Sören Kuklau
źródło
10

Problem, o którym nie wspomniano przy użyciu magicznych liczb ...

Jeśli masz ich bardzo wiele, szanse są dość dobre, że masz dwa różne cele , do których używasz magicznych liczb, w których wartości są takie same.

A potem, oczywiście, musisz zmienić wartość ... tylko w jednym celu.


źródło
Nie wydaje się to zbyt prawdopodobne, gdy mówimy o liczbach (przynajmniej nie dla mnie), ale natknąłem się na to z ciągami znaków i jest to hit: najpierw musisz przeczytać dużo kodu, aby zobaczyć, gdzie jest używany, niż ty muszę zauważyć, że jest używany do różnych rzeczy ... nie do moich ulubionych rozrywek.
Tomislav Nakic-Alfirevic
4

Zakładam, że jest to odpowiedź na moją odpowiedź na twoje wcześniejsze pytanie. W programowaniu magiczna liczba jest wbudowaną stałą numeryczną, która pojawia się bez wyjaśnienia. Jeśli pojawia się w dwóch różnych lokalizacjach, może prowadzić do okoliczności, w których jedna instancja jest zmieniana, a nie inna. Z obu tych powodów ważne jest wyodrębnienie i zdefiniowanie stałych liczbowych poza miejscami, w których są używane.

Kyle Cronin
źródło
3

Zawsze używałem terminu „magiczna liczba” inaczej, jako niejasna wartość przechowywana w strukturze danych, którą można zweryfikować jako szybkie sprawdzenie poprawności. Na przykład pliki gzip zawierają 0x1f8b08 jako pierwsze trzy bajty, pliki klasy Java zaczynają się od 0xcafebabe itp.

Często widzisz magiczne liczby osadzone w formatach plików, ponieważ pliki mogą być wysyłane dość obiecująco i tracą wszelkie metadane dotyczące sposobu ich tworzenia. Jednak magiczne liczby są również czasami używane w strukturach danych w pamięci, takich jak wywołania ioctl ().

Szybkie sprawdzenie magicznej liczby przed przetworzeniem pliku lub struktury danych pozwala wcześnie zasygnalizować błędy, a nie przechodzić przez całe potencjalnie długie przetwarzanie, aby ogłosić, że dane wejściowe były kompletne.

DGentry
źródło
2

Warto zauważyć, że czasami potrzebujesz nieskonfigurowalnych „zakodowanych” liczb w swoim kodzie. Istnieje wiele znanych tym 0x5F3759DF, który jest wykorzystywany w zoptymalizowanym algorytmie odwrotnego pierwiastka kwadratowego.

W rzadkich przypadkach, w których uważam za konieczne użycie takich magicznych liczb, ustawiam je jako stałą w moim kodzie i dokumentuję, dlaczego są używane, jak działają i skąd pochodzą.

Rob Rolnick
źródło
1
Moim zdaniem zapach magicznej liczby odnosi się konkretnie do niewyjaśnionych stałych. Dopóki umieszczasz je w nazwanej stałej, nie powinno to stanowić problemu.
Don Kirkby,
2

Co powiesz na zainicjowanie zmiennej na szczycie klasy z wartością domyślną? Na przykład:

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

W tym przypadku 15000 to magiczna liczba (zgodnie z CheckStyles). Dla mnie ustawienie wartości domyślnej jest w porządku. Nie chcę robić:

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

Czy to utrudnia czytanie? Nigdy nie zastanawiałem się nad tym, dopóki nie zainstalowałem CheckStyles.

Askaloński
źródło
Myślę, że byłoby dobrze, gdyby konstruktor zainicjował wartość. W przeciwnym razie, jeśli wartość zostanie zainicjowana poza konstruktorem, po prostu postrzegam ją jako problem i coś trudniejszego do odczytania.
Thomas Eding
Myślę, że static finalstałe są nadmierne, gdy używasz ich w jednej metodzie. finalZmienna zadeklarowana w górę metoda jest bardziej czytelny IMHO.
Eva,
0

@ eed3si9n: Sugerowałbym nawet, że „1” to magiczna liczba. :-)

Zasadą związaną z magicznymi liczbami jest to, że każdy fakt, którym zajmuje się Twój kod, powinien być zadeklarowany dokładnie raz. Jeśli użyjesz magicznych liczb w swoim kodzie (takich jak przykład długości hasła podany przez @marcio, możesz łatwo zduplikować ten fakt, a gdy zrozumiesz ten fakt, masz problem z konserwacją.

Andrzej
źródło
5
Kod IOW powinien być napisany w następujący sposób:factorial n = if n == BASE_CASE then BASE_VALUE else n * factorial (n - RECURSION_INPUT_CHANGE); RECURSION_INPUT_CHANGE = 1; BASE_CASE = 0; BASE_VALUE = 1
Thomas Eding
0

Co ze zmiennymi zwracanymi?

W szczególności wdrażanie procedur przechowywanych stanowi dla mnie wyzwanie .

Wyobraź sobie następną procedurę składowaną (wiem, zła składnia, żeby pokazać przykład):

int procGetIdCompanyByName(string companyName);

Zwraca identyfikator firmy, jeśli istnieje w określonej tabeli. W przeciwnym razie zwraca -1. Jakoś to magiczna liczba. Niektóre z rekomendacji, które przeczytałem do tej pory, mówią, że naprawdę będę musiał zaprojektować coś takiego:

int procGetIdCompanyByName(string companyName, bool existsCompany);

Nawiasem mówiąc, co powinien zwrócić, jeśli firma nie istnieje? Ok: ustawia istnienie Firma jako fałsz , ale również zwróci -1.

Opcją Antoher jest utworzenie dwóch osobnych funkcji:

bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);

Zatem warunkiem wstępnym drugiej procedury składowanej jest istnienie firmy.

Ale boję się współbieżności, ponieważ w tym systemie firma może zostać utworzona przez innego użytkownika.

Nawiasem mówiąc: co sądzisz o używaniu tego rodzaju „magicznych liczb”, które są stosunkowo znane i bezpieczne, aby powiedzieć, że coś się nie udaje lub że coś nie istnieje?

Oskytar
źródło
W tym konkretnym przypadku, jeśli dokumentacja funkcji stwierdza, że ​​ujemna wartość zwracana oznacza, że ​​nie znaleziono firmy, nie ma powodu, aby używać stałej.
Vincent Fourmond,
-1

Kolejną zaletą wyodrębnienia magicznej liczby jako stałej jest możliwość wyraźnego udokumentowania informacji biznesowych.

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();
         }
    }
}
jguiraud
źródło