Czy warto zakodować wartości na stałe w naszych aplikacjach? Czy też zawsze dobrze jest wywoływać te typy wartości dynamicznie, na wypadek gdyby trzeba było je zmienić?
Komentarze
Odpowiedz
Tak, ale niech będzie oczywiste .
Zrób:
- użyj stałych
- użyj opisowej nazwa zmiennej
Nie „t:
- nie miej żadnych magicznych liczb unoszące się wokół kodu
Komentarze
- Czystszy,
diameter = 2 * radius
lubdiameter = RADIUS_TO_DIAMETER_FACTOR * radius
? Rzeczywiście istnieją narożne przypadki, w których magiczna liczba może być lepszym rozwiązaniem. - Nie mogę ' zgodzić się z ta odpowiedź jest wystarczająca. Myślę o programowaniu jak o byciu pisarzem. Opowiadasz swoją historię za pomocą kodu i jeśli ludzie nie mogą zrozumieć logiki, moim zdaniem kod jest bezwartościowy. To ' dlatego dobrze przemyślane konwencje nazewnictwa służą przede wszystkim czytelności. Nie ma też powodu, aby używać magicznych liczb. Używając magicznych liczb, usuwasz ” dlaczego ” z równania i utrudnić jego podkreślenie tand. Na przykład: ” średnica = 2 * promień ” Do czego służą te dwa? Ten ” średnica = RADIUS_TO_DIAMETER_FACTOR * promień ” ma dużo więcej sensu.
- średnica = 2 * promień pochodzi prosto z matematyka w szkole średniej. Powodem nienazywania elementu ” 2 ” jest to, że aby miał wartość cokolwiek innego, wymagałoby zmiany prawa fizyka lub matematyka, albo jedno i drugie. (Z drugiej strony, nazwanie Pi lub stałej Plancka jest dobrym posunięciem dla prostej czytelności).
- @Joonas: Pfft. Na pewno masz na myśli
diameter = radius << 1
? Przypuszczam, że może to być równieżdiameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
. - jak ' z jakimś
diameter = radius.toDiameter()
Odpowiedź
Co wydaje mi się dziwne w tym Q & Jak dotąd, nikt tak naprawdę nie próbował jasno zdefiniować „twardego kodu” lub, co ważniejsze, alternatyw.
tl; dr : Tak, czasami jest dobrym pomysłem na stałe zakodowanie wartości, ale nie ma prostej reguły, kiedy ; to zależy całkowicie od kontekstu.
Pytanie zawęża je do wartości , które rozumiem jako magiczne liczby , ale odpowiedź na pytanie, czy „są dobrym pomysłem”, zależy od tego, do czego są faktycznie używane!
Kilka przykładów „zakodowanych „wartości to:
-
Wartości konfiguracji
Wzdrygam się, ilekroć widzę wypowiedzi na przykład
command.Timeout = 600
. Dlaczego 600? Kto o tym zdecydował? Czy wcześniej upłynął limit czasu, a ktoś podniósł limit czasu jako włamanie, zamiast naprawiać podstawowy problem z wydajnością? A może faktycznie jest to jakieś znane i udokumentowane oczekiwanie dotyczące czasu przetwarzania?Nie powinny to być magiczne liczby ani stałe, powinny być umieszczane na zewnątrz w pliku konfiguracyjnym lub bazie danych gdzieś z znacząca nazwa, ponieważ ich optymalna wartość jest w dużej mierze lub całkowicie określana przez środowisko, w którym działa aplikacja.
-
Wzory matematyczne
Wzory zazwyczaj są dość statyczne, przez co natura wartości stałych wewnątrz nie jest szczególnie ważna. 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
jest prawdopodobnie lepsze niżdiameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
– ale to fałszywa dychotomia.To, co powinieneś zrobić w tego typu scenariuszu, to utworzenie funkcji . Nie muszę wiedzieć, jak wymyśliłeś wzór, ale nadal muszę wiedzieć, do czego to służy . Jeśli zamiast któregokolwiek z nonsensów opisanych powyżej napiszę
volume = GetVolumeOfPyramid(base, height)
, wtedy nagle wszystko staje się dużo jaśniejsze i całkowicie w porządku jest mieć magiczne liczby wewnątrz funkcja (return base * height / 3
), ponieważ jest oczywiste, że są one tylko częścią formuły.Kluczem jest oczywiście posiadanie krótkie i proste funkcje. Nie działa to dla funkcji z 10 argumentami i 30 wierszami obliczeń. W takim przypadku użyj kompozycji funkcji lub stałych.
-
Reguły domeny / biznesowe
Ten obszar jest zawsze szarym obszarem, ponieważ zależy to od dokładnej wartości. W większości to właśnie te szczególne liczby magiczne są kandydatami do przekształcenia w stałe, ponieważ ułatwia to zrozumienie programu bez komplikowania jego logiki. Rozważ test
if Age < 19
aif Age < LegalDrinkingAge
; prawdopodobnie możesz dowiedzieć się, co się dzieje bez stałej, ale jest to łatwiejsze dzięki opisowemu tytuł.Mogą one także stać się kandydatami do abstrakcji funkcji, na przykład
function isLegalDrinkingAge(age) { return age >= 19 }
. Jedyną rzeczą jest to, że często logika biznesowa o wiele bardziej zawiłe i może nie mieć sensu rozpoczynanie pisania dziesiątek funkcji z 20-30 parametrami każda. Jeśli nie ma jasnej abstrakcji opartej na obiektach i / lub funkcjach, to używanie stałych jest OK.Zastrzeżenie jest takie, że jeśli „pracujesz dla działu podatkowego, pisanie
AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
staje się naprawdę, naprawdę uciążliwe i szczerze bezcelowe. Nie zamierzasz tego zrobić t, „idziesz doAttachForm("B-46")
, ponieważ każdy programista, który kiedykolwiek pracował lub kiedykolwiek tam będzie pracował, będzie wiedział, że„ B-46 ”to kod formularza dla pojedynczego podatnika wypełnianie bla bla bla – kody formularzy są częścią samej domeny, nigdy się nie zmieniają, więc „nie są tak naprawdę magicznymi liczbami.Dlatego w logice biznesowej musisz używać stałych oszczędnie; w zasadzie musisz zrozumieć, czy ta „magiczna liczba” jest faktycznie liczbą magiczną, czy też jest dobrze znanym aspektem domeny. Jeśli jest to domena, nie kodujesz jej programowo, chyba że jest naprawdę duża szansa, że to się zmieni.
-
Kody błędów i flagi stanu
Tych nigdy nie da się zakodować na stałe, co może ci powiedzieć każdy biedny drań, który kiedykolwiek został trafiony
Previous action failed due to error code 46
. Jeśli twój język to obsługuje, powinieneś używać typu wyliczenia. W przeciwnym razie zazwyczaj będziesz mieć cały plik / moduł pełen stałych określających prawidłowe wartości dla określonego typu błędu.Nigdy nie pokazuj mi
return 42
w programie obsługi błędów, capiche? Żadnych wymówek.
Prawdopodobnie pominąłem kilka scenariuszy, ale myślę, że to obejmuje większość z nich.
Więc tak, czasami jest to akceptowalna praktyka do sztywnego kodu. Po prostu nie bądź leniwy; powinna to być świadoma decyzja, a nie zwykły stary, niechlujny kod.
Komentarze
- Dzięki za dobry podział! – większość ludzi nie ' nie przemyśla wszystkich opcji, które dodałbym ” Konfiguracja środowiska ” – Myślę, że należy ich unikać (nie zakodować na stałe), ponieważ większość danych powinna być umieszczona w pliku konfiguracyjnym lub bazie danych. Jest to zgodne z zasadą ” oddzielenia danych i logiki „, która jest podstawą MVC lub MVVM. string TestServerVar = ” foo „; string ProdServerVal = ” bar „;
Odpowiedź
Istnieje wiele powodów, dla których warto przypisać identyfikator do numeru.
- Jeśli numer może się zmienić, powinien mieć identyfikator. Znacznie łatwiej jest znaleźć NUMBER_OF_PLANETS niż szukać każdego wystąpienia 9 i zastanowić się, czy powinno zostać zmienione na 8. (Zwróć uwagę, że widoczne dla użytkownika ciągi znaków mogą ulec zmianie, jeśli oprogramowanie kiedykolwiek będzie musiało być używane w innym języku, a „trudno to przewidzieć z góry”.
- Jeśli liczba jest trudna do wpisania w jakikolwiek sposób. W przypadku stałych, takich jak pi, lepiej jest podać jedną definicję o maksymalnej precyzji, niż wpisywać ją ponownie w kilku miejscach, prawdopodobnie niedokładnie.
- Jeśli liczba występuje w różnych miejscach. Nie musisz analizować dwóch zastosowań liczby 45 w przyległych funkcjach i zastanawiać się, czy oznaczają to samo.
- Jeśli znaczenie nie jest „t natychmiast rozpoznawalne”. Można bezpiecznie założyć, że wszyscy wiedzą, czym 3.14159265 … jest. Nie można bezpiecznie zakładać, że każdy rozpozna stałą grawitacji, a nawet pi / 2. („Każdy” zależy tutaj od rodzaju oprogramowania. Można oczekiwać, że od programistów systemów można oczekiwać ósemkowej reprezentacji bitów uprawnień Uniksa itp. W oprogramowaniu do architektury morskiej / morskiej, sprawdzenie liczby Froudea proponowanego kadłuba i prędkości zobacz, czy wersja 1.1 lub nowsza może być całkowicie oczywista dla każdego, kto powinien nad nią pracować.)
- Jeśli kontekst nie jest rozpoznawalny . Każdy wie, że godzina wynosi 60 minut, ale mnożenie lub dzielenie przez 60 może być niejasne, jeśli nie ma bezpośrednich wskazań, że ilość jest wartością czasu lub wartością stawki .
To daje nam kryteria dla literałów zakodowanych na stałe. Powinny być niezmienne, nietrudne do wpisania, występować tylko w jednym miejscu lub kontekście i mieć rozpoznawalne znaczenie. Nie ma sensu w definiowaniu na przykład 0 jako ARRAY_BEGINNING lub 1 jako ARRAY_INCREMENT.
Odpowiedź
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 chcesz uzyskać wszystkie wyniki z określonej tabeli, zawsze)
Poza tym użyj stałych dla dowolnej liczby innej niż 0 (zwykle). Jeśli potrzebujesz maska bitowa uprawnień równa 255, nie używaj „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 oprócz stałych wiesz, kiedy używać modułów wyliczających. Powyższy przypadek prawdopodobnie pasowałby dobrze do jednego.
Komentarze
- typedef enum {state_0 = 0, state_1 = 1, state_2 = 2, .. .} … Nie ' nie śmiej się, ' widziałem, jak to się robi. Uderz tę osobę w głowę mokrą rybą!
- @quickly well, oczywiście, ' chciałbyś czegoś bardziej podobnego do
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
- THIS_NAMING_CONVENTION_IS_RECOMMENDED_FOR_CONSTANTS
- W przypadku łańcuchów nie ' nie potrzebujesz tylko stałych. Chcesz umieścić wszystkie widoczne dla użytkownika ciągi znaków w jakimś pliku zasobów (szczegóły zależą od Twojej platformy), aby móc łatwo zmienić język na inny.
- Możesz również trzymać się logiki biznesowej ciągi (takie jak zapytania SQL) w pliku zasobów z pewnym rodzajem szyfrowania lub zaciemniania. Zapobiegnie to ” zaciekawieni ” użytkownikom przed odtworzeniem logiki (lub schematu bazy danych).
Odpowiedź
To zależy od tego, co uważasz za sztywne. Jeśli spróbujesz uniknąć wszelkich zapisanych na stałe rzeczy, trafisz na terytorium softcoding i stworzysz system, którym może zarządzać tylko twórca (i to jest ultimate hardcode)
Wiele rzeczy jest zakodowanych na sztywno w rozsądnych ramach i działają, tj. nie ma żadnego technicznego powodu, dla którego nie powinienem być w stanie zmienić punktu wejścia aplikacji C # (static void Main ), ale kodowanie na stałe, które nie stwarza żadnych problemów dla żadnego użytkownika (z wyjątkiem sporadycznego pytania SO )
Praktyczna reguła, której używam, to że wszystko, co może i ulegnie zmianie bez wpływu na stan całego systemu, powinno być konfigurowalne.
Więc IMHO, „głupio jest nie kodować na stałe rzeczy, które nigdy się nie zmieniają (pi, stała grawitacyjna, stała we wzorze matematycznym – pomyśl o objętości kuli).
Głupio jest również nie kodować na stałe rzeczy lub procesów, które będą miały wpływ na twój system, który będzie wymagał programowania w każdym przypadku, i .e. Nie ma sensu zezwalać użytkownikowi na dodawanie dynamicznych pól do formularza, jeśli jakiekolwiek dodane pole wymagałoby od dewelopera utrzymania ruchu i napisania skryptu, który sprawi, że to zadziała. Ponadto tworzenie narzędzia konfiguracyjnego jest głupie (a widziałem to kilka razy w środowiskach korporacyjnych), więc nic nie jest zakodowane na sztywno, ale tylko programiści z działu IT mogą z niego korzystać i jest to tylko trochę łatwiejsze używać go niż robić to w Visual Studio.
Podsumowując, to, czy coś powinno być zakodowane na stałe, jest funkcją dwóch zmiennych:
- czy wartość się zmieni
- jak zmiana wartości wpłynie na system
Odpowiedź
Czy warto zakodować wartości na stałe w naszych aplikacjach?
Koduję na stałe wartości tylko jeśli wartości są określone w Specyfikacji (w ostatecznej wersji 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 określiłem specyfikacje, jest to, że zmiana stałych w specyfikacjach wymaga zarządzania zmianami, w którym interesariusze dokonają przeglądu zmiany i zatwierdzą / odrzucą. Nigdy nie dzieje się to z dnia na dzień, a zatwierdzenie zajmuje miesiące / lata. Nie zapominaj, że wielu programistów używa specyfikacji (np. HTTP), więc ich zmiana oznacza uszkodzenie milionów systemów.
Odpowiedź
- jeśli wartość może się zmienić, a nawet może się zmienić, zakoduj ją programowo, gdy tylko jest to możliwe, o ile wysiłek nie przekracza oczekiwanego zwrotu
- niektóre wartości nie mogą zakodowane programowo; postępuj zgodnie z wytycznymi Jonathana w tych (rzadkich) przypadkach
Odpowiedź
Zauważyłem że za każdym razem, gdy możesz wydobyć dane z kodu, poprawia to, co zostało. Zaczynasz zauważać nowe refaktoryzacje i ulepszać całe sekcje swojego kodu.
Po prostu dobrym pomysłem jest wyodrębnianie stałych, nie traktuj tego jak głupią regułę, myśl o tym jako o okazji do kodowania lepiej.
Największą zaletą byłby sposób, w jaki można znaleźć podobne stałe, które są jedyną różnicą w grupach kodu – abstrakcja ich w tablice pomogła mi zmniejszyć niektóre pliki o 90% ich rozmiaru i naprawić całkiem w międzyczasie kilka kopii & wklejanie błędów.
Jeszcze nie widziałem jednej korzyści z niewyodrębniania danych.
Odpowiedź
Niedawno zakodowałem funkcję MySQL, aby poprawnie obliczyć odległość między dwiema parami szerokości i długości. Nie można po prostu zrobić pythagorusa; linie długości geograficznej zbliżają się do siebie wraz ze wzrostem szerokości geograficznej w kierunku biegunów, więc w grę wchodzi jakiś włochaty trygon. Chodzi o to, że byłem dość rozdarty, czy nie zakodować na stałe wartości reprezentującej promień Ziemi w milach.
Skończyło się na tym, że zrobiłem to, chociaż faktem jest, że linie szerokości / długości są znacznie bliżej siebie, powiedzmy, na Księżycu. A moja funkcja drastycznie zaniżałaby odległości między punktami na Jowiszu. Doszedłem do wniosku, że prawdopodobieństwo, że witryna, którą buduję, ma wpisaną lokalizację pozaziemską, jest dość niewielka.
Komentarze
- Tak, prawdopodobnie, ale co około google.com/moon
Odpowiedź
Cóż, to zależy od tego, czy Twój język jest skompilowany. Jeśli nie jest skompilowany, to nic wielkiego, po prostu edytuj kod źródłowy, nawet jeśli będzie on nieco delikatny dla nieprogramisty.
Jeśli programujesz w języku kompilowanym, to zdecydowanie nie jest dobry pomysł, ponieważ jeśli zmienne ulegną zmianie, musisz ponownie skompilować, co jest wielką stratą czasu, jeśli chcesz dostosować tę zmienną.
Nie musisz tworzyć suwaka ani interfejsu, aby dynamicznie zmieniać jego zmienną, ale przynajmniej możesz zrobić plik tekstowy.
Na przykład w moim projekcie ogrów zawsze używam klasa ConfigFile, aby załadować zmienną, którą zapisałem do pliku konfiguracyjnego.
Odpowiedź
Dwa razy, gdy stałe są (przynajmniej moim zdaniem) OK:
-
Stałe, które nie odnoszą się do niczego innego; możesz zmienić te stałe, kiedy tylko chcesz, 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ąpienie7
stałąDAYS_PER_WEEK
prawie nie daje żadnej wartości.
Odpowiedź
Całkowicie zgadzam się z Jonathanem, ale 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 opisowego kontekstu, powinny być odzwierciedlone jako takie w kodzie. Jeśli magiczne liczby pozostają w kodzie, należy dołożyć wszelkich starań, aby je wyodrębnić i uczynić je wyraźnie powiązanymi z ich punktem pochodzenia.
Wykonałem kilka kontraktów, w których konieczne jest wypełnienie wiadomości zmapowanymi wartościami z bazy danych. W większości przypadków mapowanie jest dość proste i pasowałoby do ogólnych wytycznych Jonathana, ale spotkałem się z przypadkami, w których struktura wiadomości docelowej była po prostu okropna.Ponad 80% wartości, które musiały zostać przekazane w strukturze, to stałe wymuszone specyfikacją odległego systemu. w połączeniu z faktem, że struktura wiadomości była olbrzymia, sprawiło, że trzeba było wypełnić DUŻO takich stałych. W większości przypadków nie podały znaczenia ani powodu, po prostu powiedziały „wstaw M tutaj” lub „wstaw 4.10.53.10100.889450.4452 tutaj”. Nie próbowałem też umieszczać komentarza przy każdym z nich, co spowodowałoby, że wynikowy kod byłby nieczytelny. Jednak upewniłem się, że sekcje kodu, w których pojawiają się te magiczne wartości, są odpowiednio odizolowane i że ich kontenery (klasy, pakiety) są odpowiednio nazwane, aby wskazywały bezpośrednio na wymuszającą je specyfikację.
To powiedziawszy, kiedy myślisz o to … chodzi głównie o uczynienie tego oczywistym …
Odpowiedź
Jeśli „zakodujesz na stałe wartość stałej grawitacyjnej Ziemi”, nikogo to nie obchodzi. Jeśli zakodujesz na stałe adres IP serwera proxy, „możesz mieć kłopoty.
Komentarze
- Możesz potrzebować większej precyzji w przypadku ziemi ' stałej grawitacji, więc zakodowanie jej kilka razy na sztywno może prowadzić do problemów.
- Peter Noone? ?
- Przyspieszenie grawitacyjne na Ziemi wynosi prawie 9,81 m / s ^ 2 dla większości szerokości i wysokości (oczywiście, jeśli ' poszukujesz ropy pod ziemią lub strzelanie międzykontynentalne rakiety balistyczne nad Biegunem Północnym, wiedząc, że zmiana grawitacji jest bardzo ważna dla znacznie większej liczby miejsc po przecinku), przy czym przyspieszenie grawitacyjne na innych planetach ma inną liczbę, ale o ile wiem, stała grawitacyjna jest stała wokół wszechświata. Jest wiele elementów fizycznych, które musiałyby ulec zmianie, gdyby g było zmienne.
Odpowiedź
W większości nie, ale myślę, że warto zauważyć, że jesteś Najwięcej problemów będę miał, gdy zaczniesz kopiować zakodowaną wartość. Jeśli tego nie powiesz (np. Użyjesz go tylko raz w implementacji klasy), to nieużywanie stałej może być w porządku.
pi
może się zmienić …