Co to jest magiczna liczba?
Dlaczego należy tego unikać?
Czy istnieją przypadki, w których jest to właściwe?
language-agnostic
terminology
magic-numbers
Adam Davis
źródło
źródło
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.Odpowiedzi:
Magiczna liczba to bezpośrednie użycie liczby w kodzie.
Na przykład, jeśli masz (w Javie):
Należy to zmienić na:
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
,Character
iMath
klas.PS: Narzędzia analizy statycznej, takie jak FindBugs i PMD, wykrywają użycie magicznych liczb w kodzie i sugerują refaktoryzację.
źródło
TRUE
/FALSE
)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 = 50
połą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 = 25
LubTCPPacketSize = 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.źródło
SmtpClient.DefaultPort = 25
jest możliwie jasne er niżSmtpClient.DefaultPort = DEFAULT_SMTP_PORT
.25
całej aplikacji i upewnić się, że zmienisz tylko te wystąpienia,25
które dotyczą portu SMTP, a nie 25, które są np. Szerokością kolumny tabeli lub liczbą rekordów do wyświetlenia na stronie.IANA
.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
źródło
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
Podstawy
Mamy dwa scenariusze dla naszych magicznych podstawowych wartości. Tylko druga kwestia ma podstawowe znaczenie dla programistów i kodu:
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
jest:
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 = 2
nie jest wystarczająco dobre. Pojęcie „domyślnie” nie zostało ujawnione. Dopiero gdy piszę:padding = default_padding
jako kontekst, a potem gdzie indziej: wdefault_padding = 2
peł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_padding
GUI 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 symbolicznadefault_padding
w kontekście GUI UX „mojego programu”, aby można było z niej korzystać takdefault_padding
szybko, 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: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 jakattack_elves
lubseek_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 ...
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.
źródło
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.
źródło
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.
źródło
(foo[i]+foo[i+1]+foo[i+2]+1)/3
może być oceniane znacznie szybciej niż pętla. Jeśli ktoś zastąpi3
kod bez przepisywania kodu jako pętli, ktoś, kto widziałITEMS_TO_AVERAGE
zdefiniowany jako,3
może pomyśleć, że może go zmienić5
i mieć średni kod więcej elementów. Natomiast ktoś, kto spojrzy na wyrażenie dosłowne3
, zda sobie sprawę, że3
reprezentuje liczbę sumowanych elementów.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. ;-)
źródło
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
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.
źródło
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.
źródło
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ą.
źródło
Co powiesz na zainicjowanie zmiennej na szczycie klasy z wartością domyślną? Na przykład:
W tym przypadku 15000 to magiczna liczba (zgodnie z CheckStyles). Dla mnie ustawienie wartości domyślnej jest w porządku. Nie chcę robić:
Czy to utrudnia czytanie? Nigdy nie zastanawiałem się nad tym, dopóki nie zainstalowałem CheckStyles.
źródło
static final
stałe są nadmierne, gdy używasz ich w jednej metodzie.final
Zmienna zadeklarowana w górę metoda jest bardziej czytelny IMHO.@ 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ą.
źródło
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
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):
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:
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:
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?
źródło
Kolejną zaletą wyodrębnienia magicznej liczby jako stałej jest możliwość wyraźnego udokumentowania informacji biznesowych.
źródło