Czy warto zawsze wpisywać wartości w naszych aplikacjach? Czy też zawsze właściwym jest dynamiczne wywoływanie tego rodzaju wartości w przypadku, gdy trzeba je zmienić?
pi
może się zmienić ...
Czy warto zawsze wpisywać wartości w naszych aplikacjach? Czy też zawsze właściwym jest dynamiczne wywoływanie tego rodzaju wartości w przypadku, gdy trzeba je zmienić?
pi
może się zmienić ...
Odpowiedzi:
Tak, ale czyń to oczywistym .
Zrobić:
Nie:
diameter = 2 * radius
lub diameter = RADIUS_TO_DIAMETER_FACTOR * radius
? Rzeczywiście istnieją przypadki narożne, w których magiczna liczba może być lepszym rozwiązaniem.
diameter = radius << 1
? Myślę, że to też może być diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
.
diameter = radius.toDiameter()
Co dziwne w moich pytaniach do tej pory, to fakt, że nikt tak naprawdę nie próbował jasno zdefiniować „twardego kodu” lub, co ważniejsze, alternatyw.
tl; dr: Tak, to jest czasami dobrym pomysłem wartościach ciężko kod, ale nie ma prosta zasada jak do kiedy ; zależy to całkowicie od kontekstu.
Pytanie ogranicza się do wartości , które rozumiem jako liczby magiczne , ale odpowiedź na pytanie, czy są dobrym pomysłem, zależy od tego, do czego są używane!
Kilka przykładów wartości „zakodowanych na stałe” to:
Wartości konfiguracyjne
Kulę się, gdy widzę takie stwierdzenia command.Timeout = 600
. Dlaczego 600? Kto to postanowił? Czy upłynął już limit czasu i ktoś podniósł limit czasu jako hack zamiast naprawić podstawowy problem z wydajnością? A może jest to jakieś znane i udokumentowane oczekiwanie na czas przetwarzania?
Nie powinny to być magiczne liczby ani stałe, należy je uzewnętrznić w pliku konfiguracyjnym lub bazie danych gdzieś o sensownej nazwie, ponieważ ich optymalna wartość zależy w dużej mierze lub w całości od środowiska, w którym działa aplikacja.
Wzory matematyczne
Formuły zwykle bywają dość statyczne, tak że charakter stałych wartości w środku nie jest tak naprawdę szczególnie ważny. Objętość piramidy wynosi (1/3) b * h. Czy obchodzi nas, skąd pochodzi 1 lub 3? Nie całkiem. Poprzedni komentator słusznie zauważył, że diameter = radius * 2
prawdopodobnie jest to lepsze niż diameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
- ale jest to fałszywa dychotomia.
To, co powinieneś zrobić dla tego typu scenariusza, to utworzenie funkcji . Nie muszę wiedzieć, jak wymyśliłeś tę formułę, ale wciąż muszę wiedzieć, do czego ona służy . Jeśli zamiast któregokolwiek z powyższych nonsensów piszę, volume = GetVolumeOfPyramid(base, height)
nagle wszystko staje się o wiele wyraźniejsze, i całkiem dobrze jest mieć magiczne liczby wewnątrz funkcji ( return base * height / 3
), ponieważ jest oczywiste, że są one tylko częścią formuły.
Kluczem tutaj są oczywiście krótkie i proste funkcje. Nie działa to w przypadku funkcji z 10 argumentami i 30 wierszami obliczeń. W takim przypadku użyj kompozycji funkcji lub stałych.
Zasady domeny / biznesu
Ten jest zawsze szarym obszarem, ponieważ zależy od tego, jaka jest dokładnie wartość. W większości przypadków te konkretne magiczne liczby są kandydatami do przekształcenia się w stałe, ponieważ dzięki temu program jest łatwiejszy do zrozumienia bez komplikowania logiki programu. Rozważmy testową if Age < 19
Vs. if Age < LegalDrinkingAge
; prawdopodobnie możesz dowiedzieć się, co się dzieje bez stałej, ale z opisowym tytułem jest to łatwiejsze.
Mogą również stać się kandydatami na przykład do abstrakcji funkcji function isLegalDrinkingAge(age) { return age >= 19 }
. Jedyną rzeczą jest to, że często twoja logika biznesowa jest o wiele bardziej skomplikowana i może nie mieć sensu pisanie dziesiątek funkcji o 20-30 parametrach każda. Jeśli nie ma wyraźnej abstrakcji opartej na obiektach i / lub funkcjach, odwoływanie się do stałych jest OK.
Zastrzeżenie polega na tym, że jeśli pracujesz dla działu podatkowego, pisanie staje się naprawdę uciążliwe i naprawdę bezcelowe AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
. Nie zamierzacie tego robić, zamierzacie to zrobić, AttachForm("B-46")
ponieważ każdy programista, który kiedykolwiek pracował lub kiedykolwiek będzie pracował, będzie wiedział, że „B-46” to kod formularza dla jednego podatnika składającego bla bla bla kody formularzy są częścią samej domeny, nigdy się nie zmieniają, więc nie są tak naprawdę magicznymi liczbami.
Musisz więc oszczędnie używać stałych w logice biznesowej; w zasadzie musisz zrozumieć, czy ta „magiczna liczba” jest w rzeczywistości liczbą magiczną, czy też jest dobrze znanym aspektem domeny. Jeśli jest to domena, to nie kodujesz jej, chyba że istnieje naprawdę duża szansa, że się zmieni.
Kody błędów i flagi stanu
Są to nie w porządku, aby twardym kodu, jak każdy biedny drań, który nigdy nie został hitem Previous action failed due to error code 46
mogę ci powiedzieć. Jeśli Twój język to obsługuje, powinieneś używać typu wyliczenia. W przeciwnym razie zwykle będziesz mieć cały plik / moduł pełen stałych określających prawidłowe wartości dla określonego typu błędu.
Czy nigdy nie pozwalasz mi zobaczyć return 42
w module obsługi błędów, capiche? Bez wymówek.
Prawdopodobnie pominąłem kilka scenariuszy, ale myślę, że obejmuje większość z nich.
Tak, tak, czasami akceptowalną praktyką jest kodowanie na stałe. Po prostu nie bądź leniwy; powinna to być świadoma decyzja, a nie zwykły niechlujny kod.
Istnieje wiele powodów przypisywania identyfikatora do numeru.
To daje nam kryteria dla literałów na stałe. Powinny być niezmienne, nietrudne do wpisania, występować tylko w jednym miejscu lub kontekście i mieć rozpoznawalne znaczenie. Na przykład nie ma sensu definiować 0 jako ARRAY_BEGINNING lub 1 jako ARRAY_INCREMENT.
Jako dodatek do innych odpowiedzi. Jeśli to możliwe, używaj stałych dla łańcuchów. Oczywiście nie chcesz tego mieć
const string server_var="server_var";
ale powinieneś mieć
const string MySelectQuery="select * from mytable;";
(zakładając, że faktycznie masz zapytanie, w którym zawsze chcesz uzyskać wszystkie wyniki z określonej tabeli)
Poza tym używaj stałych dla dowolnej liczby innej niż 0 (zwykle). Jeśli potrzebujesz maski bitowej uprawnień 255, nie używaj
const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.
zamiast tego użyj
const int AllowGlobalRead=255;
Oczywiście wraz ze stałymi wiemy, kiedy używać enumeratorów. Powyższy przypadek prawdopodobnie pasowałby do jednego.
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
To zależy od tego, co uważasz za twarde. Jeśli starasz się unikać wszelkich rzeczy zakodowanych na stałe, trafiasz na terytorium kodowania miękkiego i tworzysz system, którym może zarządzać tylko twórca (i to jest ostateczny twardy kod )
Wiele rzeczy jest zakodowanych na sztywno w rozsądnych ramach i działają. tzn. nie ma technicznego powodu, dla którego nie powinienem być w stanie zmienić punktu wejścia aplikacji C # (static void Main), ale twarde kodowanie, które nie stwarza żadnych problemów dla żadnego użytkownika (z wyjątkiem sporadycznych pytań SO )
Zasadą, której używam jest to, że wszystko, co może i zmieni się, bez wpływu na stan całego systemu, powinno być konfugurowalne.
Więc, IMHO, głupio jest nie kodować rzeczy, które nigdy się nie zmieniają (pi, stała grawitacyjna, stała we wzorze matematycznym - myśl objętość kuli).
Głupio jest też nie kodować rzeczy lub procesów, które będą miały wpływ na twój system, który w każdym przypadku będzie wymagał programowania, tzn. Nie jest możliwe, aby użytkownik mógł dodawać pola dynamiczne do formularza, jeśli jakiekolwiek dodane pole wymagałoby od programisty konserwacji wejdź i napisz skrypt, który sprawi, że to zadziała. Głupie jest (i widziałem to kilka razy w środowiskach korporacyjnych) tworzenie jakiegoś narzędzia konfiguracyjnego, więc nic nie jest zakodowane, ale tylko programiści z działu IT mogą z niego korzystać, a korzystanie z niego jest tylko odrobinę łatwiejsze niż zrobić to w Visual Studio.
Zatem kwestia, czy rzecz powinna zostać zakodowana na stałe, jest funkcją dwóch zmiennych:
Czy warto zawsze wpisywać wartości w naszych aplikacjach?
Podaję wartości kodu tylko wtedy, gdy wartości są określone w specyfikacji (w ostatecznym wydaniu specyfikacji), np. Odpowiedź HTTP OK zawsze będzie 200
(chyba że zmieni się w RFC), więc zobaczysz (w niektórych moich kodach ) stałe, takie jak:
public static final int HTTP_OK = 200;
W przeciwnym razie przechowuję stałe w pliku właściwości.
Powodem, dla którego podałem specyfikacje, jest to, że zmienianie stałych w specyfikacjach wymaga zarządzania zmianami, w którym interesariusze sprawdzą zmianę i zatwierdzą / odrzucą. To się nigdy nie zdarza z dnia na dzień, a zatwierdzenie zajmuje miesiące / lata. Nie zapominaj, że wielu programistów korzysta ze specyfikacji (np. HTTP), więc zmiana tego oznacza złamanie milionów systemów.
Zauważyłem, że za każdym razem, gdy możesz wyodrębnić dane ze swojego kodu, poprawia to, co zostało. Zaczynasz zauważać nowe refaktoryzacje i ulepszać całe sekcje kodu.
Dobrym pomysłem jest praca w kierunku wyodrębniania stałych, nie uważaj tego za głupią zasadę, pomyśl o tym jako o możliwości lepszego kodowania.
Największą zaletą byłby sposób, w jaki można znaleźć podobne stałe będące jedyną różnicą w grupach kodu - ich abstrakcja na tablice pomogła mi zmniejszyć niektóre pliki o 90% ich wielkości i naprawić w międzyczasie sporo błędów kopiowania i wklejania .
Jeszcze nie widziałem ani jednej korzyści, że nie wyodrębniam danych.
Niedawno kodowałem funkcję MySQL, aby poprawnie obliczyć odległość między dwiema parami lat / long. Nie możesz po prostu robić pitagoru; linie długości geograficznej zbliżają się do siebie wraz ze wzrostem szerokości geograficznej w kierunku biegunów, więc wiąże się to z pewnym włochatym wyzwalaniem. Chodzi o to, że byłem dość rozdarty, czy zakodować na stałe wartość reprezentującą promień Ziemi w milach.
Skończyło się na tym, mimo że linie lat / lng są znacznie bliżej, powiedzmy, na Księżycu. A moja funkcja drastycznie zaniżałaby odległości między punktami na Jowiszu. Doszedłem do wniosku, że szanse na to, że strona internetowa, którą buduję, będą miały dość pozaziemską lokalizację, są niewielkie.
To zależy, czy Twój język jest skompilowany. Jeśli nie jest skompilowany, to nie jest wielka sprawa, po prostu edytuj kod źródłowy, nawet jeśli będzie on nieco delikatny dla nieprogramowującego.
Jeśli programujesz przy użyciu skompilowanego języka, to zdecydowanie nie jest dobry pomysł, ponieważ jeśli zmienne się zmienią, musisz dokonać ponownej kompilacji, co jest dużą stratą czasu, jeśli chcesz dostosować tę zmienną.
Nie musisz tworzyć suwaka ani interfejsu, aby dynamicznie zmieniać jego zmienną, ale przynajmniej możesz to zrobić w postaci pliku tekstowego.
Na przykład w moim projekcie ogr zawsze używam klasy ConfigFile, aby załadować zmienną, którą zapisałem do pliku konfiguracyjnego.
Dwie sytuacje, w których stałe są (przynajmniej moim zdaniem) OK:
Stałe, które nie odnoszą się do niczego innego; możesz zmienić te stałe w dowolnym momencie, bez konieczności zmiany czegokolwiek innego. Przykład: domyślna szerokość kolumny siatki.
Absolutnie niezmienne, precyzyjne, oczywiste stałe, takie jak „liczba dni w tygodniu”. days = weeks * 7
Zastąpienie 7
stałą DAYS_PER_WEEK
prawie nie daje żadnej wartości.
Zgadzam się całkowicie z Jonathanem, ale jak wszystkie zasady są wyjątki ...
„Magiczna liczba w specyfikacji: Magiczna liczba w kodzie”
Zasadniczo stwierdza, że wszelkie magiczne liczby, które pozostają w specyfikacji po rozsądnych próbach uzyskania dla nich kontekstu opisowego, powinny zostać odzwierciedlone jako takie w kodzie. Jeśli w kodzie pozostają magiczne liczby, należy dołożyć wszelkich starań, aby je wyodrębnić i wyraźnie powiązać z miejscem ich pochodzenia.
Wykonałem kilka umów dotyczących interfejsów, w których konieczne jest wypełnienie wiadomości wartościami odwzorowanymi z bazy danych. W większości przypadków mapowanie jest dość proste i pasowałoby do ogólnych wytycznych Jonathana, ale napotkałem przypadki, w których struktura docelowego komunikatu była po prostu okropna. Ponad 80% wartości, które musiały zostać przekazane w strukturze, były stałymi wymuszonymi przez specyfikację odległego układu. To w połączeniu z faktem, że struktura wiadomości była gigantyczna, spowodowało, że DUŻO takich stałych musiała zostać zaludniona. W większości przypadków nie podali znaczenia ani powodu, po prostu powiedzieli „wstaw M tutaj” lub „wstaw 4.10.53.10100.889450.4452 tutaj”. Nie próbowałem też umieszczać komentarza obok nich wszystkich, co spowodowałoby, że wynikowy kod byłby nieczytelny.
To powiedziawszy, kiedy się nad tym zastanowić ... w zasadzie chodzi o to, aby było to oczywiste ...
Jeśli wpisujesz wartość stałej grawitacyjnej na ziemi, nikogo to nie obchodzi. Jeśli zaszyfrujesz adres IP swojego serwera proxy, masz kłopoty.
Przeważnie nie, ale myślę, że warto zauważyć, że będziesz miał najwięcej problemów, gdy zaczniesz powielać wartość zakodowaną na stałe. Jeśli go nie powielisz (np. Użyjesz go tylko raz w implementacji klasy), to niestosowanie stałej może być w porządku.