Czy warto zawsze wpisywać wartości w naszych aplikacjach?

45

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ć?

Edward
źródło
2
pomoże ci parametr konfiguracyjny
Gopi,
53
Nigdy nie wiadomo, kiedy wartość pimoże się zmienić ...
Gabe,
12
Człowieku, ludzie tacy jak @ gabe są powodem, dla którego jest to „Reguła”. Jeśli powtórzysz 3.14 w 20 miejscach w swoim kodzie, a następnie okaże się, że naprawdę potrzebujesz większej dokładności, to jesteś skończony. Nie zdawałem sobie sprawy, że to nie było oczywiste.
Bill K
17
To było trochę niegrzeczne, @Bill. @Gabe wyraźnie żartował, ale poza tym pytanie dotyczyło zakodowania na twardo vs. parametrów konfiguracyjnych, a nie używania stałych i powtarzanych magicznych liczb w wielu miejscach.
David Conrad
1
Tak, czasami kodowanie może być dobrym pomysłem. Zobacz artykuł w Wikipedii na temat anty-wzoru „Softcoding”.
user16764

Odpowiedzi:

64

Tak, ale czyń to oczywistym .

Zrobić:

  • użyj stałych
  • użyj opisowej nazwy zmiennej

Nie:

Jonathan Khoo
źródło
44
Który jest czystszy diameter = 2 * radiuslub diameter = RADIUS_TO_DIAMETER_FACTOR * radius? Rzeczywiście istnieją przypadki narożne, w których magiczna liczba może być lepszym rozwiązaniem.
Joonas Pulakka
5
Nie mogę się wystarczająco zgodzić z tą odpowiedzią. Mam tendencję do myślenia o programowaniu jak o pisarzu. Opowiadasz swoją historię za pomocą kodu, a jeśli ludzie nie rozumieją logiki, moim zdaniem kod staje się bezwartościowy. Dlatego dobrze przemyślane konwencje nazewnictwa służą przede wszystkim czytelności. Ponadto nie ma dobrego powodu, aby używać magicznych liczb. Używając magicznych liczb, usuwacie „dlaczego” z równania i utrudniacie zrozumienie. Na przykład: „średnica = 2 * promień” Do czego służą te dwa elementy? Ten „promień = średnica = RADIUS_TO_DIAMETER_FACTOR * promień” ma znacznie większy sens.
Chris
9
średnica = 2 * promień pochodzi prosto z matematyki w szkole średniej. Powodem, dla którego nie nazywa się „2”, jest fakt, że posiadanie wartości czegokolwiek innego wymagałoby zmiany praw fizyki, matematyki lub obu tych elementów. (Z drugiej strony, nazwanie Pi lub stała Plancka jest dobrym posunięciem dla prostej czytelności).
szybko_niedz.
8
@Joonas: Pfft. Na pewno masz na myśli diameter = radius << 1? Myślę, że to też może być diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT.
Ant
4
jak o niektórychdiameter = radius.toDiameter()
Carson Myers
26

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 * 2prawdopodobnie 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 < 19Vs. 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 46mogę 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 42w 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.

Aaronaught
źródło
Dzięki za dobry podział! - większość ludzi nie zastanawia się nad wszystkimi opcjami, które dodałbym „Konfiguracja środowiska” - Myślę, że należy ich unikać (nie zakodowanych na stałe), ponieważ większość danych należy umieścić w pliku konfiguracyjnym lub bazie danych. Jest to zgodne z zasadą „oddzielania danych i logiki”, która jest podstawą MVC lub MVVM. ciąg TestServerVar = "foo"; ciąg ProdServerVal = "bar";
m1m1k
7

Istnieje wiele powodów przypisywania identyfikatora do numeru.

  • Jeśli numer może się zmienić, powinien mieć identyfikator. Znacznie łatwiej jest znaleźć NUMBER_OF_PLANETS niż wyszukiwać każdą instancję 9 i zastanowić się, czy należy ją zmienić na 8. (Pamiętaj, że widoczne ciągi znaków mogą wymagać zmiany, jeśli oprogramowanie będzie musiało być używane w innym języku, i to jest trudna do przewidzenia z góry).
  • Jeśli numer jest trudny do wpisania w jakikolwiek sposób. W przypadku stałych takich jak pi lepiej jest podać jedną definicję o maksymalnej precyzji niż przepisać ją w kilku miejscach, być może niedokładnie.
  • Jeśli numer występuje w różnych miejscach. Nie powinieneś patrzeć na dwa zastosowania 45 w sąsiednich funkcjach i zastanawiać się, czy oznaczają to samo.
  • Jeśli znaczenia nie można natychmiast rozpoznać. Można bezpiecznie założyć, że każdy wie, czym jest 3.14159265 ... Nie jest bezpiecznie zakładać, że wszyscy rozpoznają stałą grawitacji, a nawet pi / 2. („Wszyscy” tutaj zależą od charakteru oprogramowania. Można się spodziewać, że programiści systemów znają ósemkową reprezentację bitów uprawnień Unix itp. W oprogramowaniu architektury morskiej / morskiej sprawdzanie liczby Froude'a proponowanego kadłuba i prędkości do sprawdź, czy jest to wersja 1.1 lub nowsza, która może być całkowicie zrozumiała dla każdego, kto powinien nad nią pracować.)
  • Jeśli kontekst nie jest rozpoznawalny. Wszyscy wiedzą, że jest 60 minut na godzinę, ale pomnożenie lub podzielenie przez 60 może być niejasne, jeśli nie ma bezpośrednich wskazań, że ilość jest wartością czasową lub wartością stawki.

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.

David Thornley
źródło
5

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.

Earlz
źródło
typedef enum {stan_0 = 0, stan_1 = 1, stan_2 = 2, ...} ... Nie śmiej się, widziałem, jak to zrobiono. Uderz tę osobę w głowę mokrą rybą!
szybko_niedz.
@ bardzo dobrze, oczywiście, że chcesz czegoś więcejtypedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Earlz
6
THIS_NAMING_CONVENTION_IS_RECOMMENDED_FOR_CONSTANTS
StuperUser
4
W przypadku łańcuchów nie chcesz tylko stałych. Chcesz umieścić ciągi widoczne dla użytkownika w jakimś pliku zasobów (szczegóły zależą od twojej platformy), abyś mógł łatwo przejść na inny język.
David Thornley,
Możesz także umieścić ciągi związane z logiką biznesową (np. Zapytania SQL) w pliku zasobów z pewnym rodzajem szyfrowania lub zaciemniania. Zapobiegnie to „ciekawym” użytkownikom odwracania logiki (lub schematu bazy danych).
TMN
4

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:

  • zmieni się wartość
  • jak zmiana wartości wpłynie na system
SWeko
źródło
4

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.

Buhake Sindi
źródło
3
  • jeśli wartość może się zmienić, a nawet może się zmienić, wówczas należy ją kodować w miarę możliwości, o ile nakład pracy nie przekracza oczekiwanego zwrotu
  • niektórych wartości nie można zakodować na miękko; postępuj zgodnie z wytycznymi Jonathana w tych (rzadkich) przypadkach
Steven A. Lowe
źródło
2

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.

Bill K.
źródło
2

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.

Dan Ray
źródło
Tak, prawdopodobnie, ale co z google.com/moon
Residuum
1

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.

żart
źródło
1

Dwie sytuacje, w których stałe są (przynajmniej moim zdaniem) OK:

  1. 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.

  2. Absolutnie niezmienne, precyzyjne, oczywiste stałe, takie jak „liczba dni w tygodniu”. days = weeks * 7Zastąpienie 7stałą DAYS_PER_WEEKprawie nie daje żadnej wartości.

użytkownik 281377
źródło
0

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 ...

Newtopian
źródło
0

Jeśli wpisujesz wartość stałej grawitacyjnej na ziemi, nikogo to nie obchodzi. Jeśli zaszyfrujesz adres IP swojego serwera proxy, masz kłopoty.

jwenting
źródło
1
Możesz potrzebować większej precyzji dla stałej grawitacyjnej Ziemi, więc kilkukrotne jej zakodowanie może prowadzić do problemów.
user281377
1
Peter Noone? Od Pustelników Hermana?
David Conrad
Przyspieszenie grawitacyjne na Ziemi wynosi prawie 9,81 m / s ^ 2 dla większości szerokości i wysokości (oczywiście, jeśli szukasz ropy pod ziemią lub strzelasz do ICBM nad biegunem północnym, wiedząc, że zmiana grawitacji jest bardzo ważna dla znacznie więcej miejsc po przecinku), przy czym przyspieszenie grawitacyjne na innych planetach jest inną liczbą, ale o ile wiem, stała grawitacyjna jest stała w całym wszechświecie. Jest dużo fizyki, która musiałaby się zmienić, gdyby g było zmienne.
Tangurena,
0

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.


źródło