Czy stałe jednoznakowe są lepsze od literałów?

127

Ostatnio spotkałem klasę, która zapewnia prawie każdą pojedynczą postać jako stałą; wszystko od COMMAdo BRACKET_OPEN. Zastanawiasz się, czy było to konieczne; Przeczytałem „artykuł”, który sugeruje, że pomocne może być ciągnięcie literałów jednoznakowych do stałych. Jestem sceptyczny.

Główną zaletą korzystania ze stałych jest to, że minimalizują konieczność konserwacji, gdy konieczna jest zmiana. Ale kiedy zaczniemy używać innego symbolu niż „,” do przedstawienia przecinka?

Jedynym powodem, dla którego widzę używanie stałych zamiast literałów, jest uczynienie kodu bardziej czytelnym. Ale czy city + CharacterClass.COMMA + state(na przykład) jest naprawdę bardziej czytelny niż city + ',' + state?

Dla mnie minusy przewyższają zalety, głównie że wprowadzasz inną klasę i kolejny import. I wierzę w mniej kodu, jeśli to możliwe. Zastanawiam się więc, jaki jest ogólny konsensus.

Dzień Austina
źródło
33
Hmm ... może to może być przydatne w różnych lokalizacjach? Na przykład niektóre języki używają guillements (cudzysłowy kątowe «i ») jako cudzysłowy zamiast standardowego języka angielskiego "(lub ładniej wyglądającego i ). Poza tym, to po prostu brzmi jak zestaw magicznych postaci. Zakładając dwa przypadki CharacterClasswywołania englishCharsi frenchChars, możliwe, englishChars.LEFT_QUOTEże tak jest , a frenchChars.LEFT_QUOTEmoże być «.
Justin Time,
4
Istnieje wiele różnych wariantów przecinków: en.wikipedia.org/wiki/Comma#Comma_variants - być może nie jest to taki głupi pomysł, szczególnie jeśli kod źródłowy można zakodować jako utf-8.
Aaron Hall,
21
W twoim przypadku jest to jak wywoływanie zmiennej „numer”. Twoja stała powinna być nazwana DELIMITER. Lub powinno być CITY_STATE = "{0}, {1}"
the_lotus
13
Ten artykuł, który podłączyłeś, jest bardzo okropny. Stałe nigdy nie powinny być wrzucane do takiego wiadra. Umieść je w klasach, w których mają kontekst: w istocie klasa ze stałą zapewnia kontekst, w którym używana jest stała. Na przykład Java File.separator. Klasa informuje o typie separatora. Posiadanie klasy o nazwie Constslub Constantsbrak kontekstu i utrudnia prawidłowe użycie stałych.

Odpowiedzi:

184

Tautologia :

Jest bardzo jasne, jeśli przeczytasz pierwsze zdanie pytania, że ​​to pytanie nie dotyczy odpowiednich zastosowań, takich jak eliminacja magicznych liczb , chodzi w najlepszym razie o okropną, bezmyślną, głupią konsekwencję. I właśnie na to odpowiada ta odpowiedź

Zdrowy rozsądek mówi, że const char UPPER_CASE_A = 'A';albo const char A = 'A'nie dodaje niczego ale utrzymanie i złożoności do systemu. const char STATUS_CODE.ARRIVED = 'A'to inna sprawa.

Stałe powinny reprezentować rzeczy, które są niezmienne w czasie wykonywania, ale mogą wymagać modyfikacji w przyszłości podczas kompilacji. Kiedy const char A =poprawnie równa się coś innego niż A?

Jeśli widzisz public static final char COLON = ':'w kodzie Java, znajdź tego, kto to napisał i złam klawiaturę. Jeśli reprezentacja na COLONzawsze zmieni się od :ciebie, będzie miał koszmar konserwacji.

Zaciemnianie:

Co się stanie, gdy ktoś go zmieni, COLON = '-'ponieważ wszędzie tam, gdzie go -używa, wszędzie jest potrzebny? Czy zamierzasz napisać testy jednostkowe, które w zasadzie mówią assertThat(':' == COLON)o każdym pojedynczym constodwołaniu, aby upewnić się, że nie zostaną zmienione? Tylko po to, żeby ktoś naprawił test, kiedy je zmieni?

Jeśli ktoś faktycznie twierdzi, że public static final String EMPTY_STRING = "";jest to przydatne i pożyteczne, po prostu kwalifikujesz jego wiedzę i bezpiecznie ignorujesz ją we wszystkim innym.

Każdy znak dostępny do wydruku w nazwanej wersji pokazuje tylko, że ktokolwiek to zrobił, nie jest uprawniony do pisania kodu bez nadzoru.

Spójność:

Sztucznie obniża także spójność, ponieważ odsuwa rzeczy od rzeczy, które ich używają i są z nimi powiązane.

W programowaniu komputerowym spójność odnosi się do stopnia, w jakim elementy modułu należą do siebie. Zatem spójność mierzy siłę związku między elementami funkcjonalności w danym module. Na przykład w bardzo spójnych systemach funkcjonalność jest ściśle powiązana.

Sprzęganie:

Łączy też wiele niepowiązanych ze sobą klas, ponieważ wszystkie kończą się odwoływaniem do plików, które tak naprawdę nie są powiązane z tym, co robią.

Ścisłe sprzężenie ma miejsce, gdy grupa klas jest od siebie wysoce zależna. Ten scenariusz powstaje, gdy klasa przyjmuje zbyt wiele obowiązków lub gdy jedna troska jest rozłożona na wiele klas, zamiast mieć własną klasę.

Jeśli użyjesz lepszej nazwy, tak DELIMITER = ','jakbyś nadal miał ten sam problem, ponieważ nazwa jest ogólna i nie ma semantycznego. Ponowne przypisanie wartości nie pomaga bardziej w analizie wpływu niż wyszukiwanie i zastępowanie literału ','. Ponieważ jaki kod wykorzystuje i potrzebuje ,innych kodów, ale potrzebuje ;teraz? Nadal muszę ręcznie sprawdzać każde użycie i je zmieniać.

W dziczy:

Niedawno odnowiłem 1,000,000+ LOCaplikację, która miała 18 lat. Miał takie rzeczy public static final COMMA = SPACE + "," + SPACE;. Nie jest to w żaden sposób lepsze niż tylko wskazanie, " , "gdzie jest potrzebne.

Jeśli chcesz się spierać o czytelność , musisz nauczyć się konfigurować IDE tak, aby wyświetlał whitespaceznaki w miejscu, w którym je widzisz, lub cokolwiek innego, jest to po prostu niezwykle leniwy powód, aby wprowadzić entropię do systemu.

To również nie ,zdefiniowano kilka razy z wieloma pisowni tego słowa COMMAw wielu pakietów i klas. Z odniesieniami do wszystkich odmian zmieszanych razem w kodzie. Próba naprawienia czegoś bez zepsucia czegoś zupełnie niezwiązanego z niczym nie była koszmarem .

To samo z alfabetu, gdy było wielu UPPER_CASE_A, A, UPPER_A, A_UPPERktóry przez większość czasu były równe A , ale w niektórych przypadkach nie było . Dla prawie każdej postaci, ale nie dla wszystkich postaci.

I z historii edycji nie wynika, że ​​jeden z nich był kiedykolwiek edytowany lub zmieniany przez 18 lat, z powodu tego, co powinno być teraz oczywistym powodem, że złamałoby zdecydowanie zbyt wiele rzeczy, których nie można wyśledzić, dlatego masz nową zmienną nazwy wskazujące na tę samą rzecz, której nigdy nie można zmienić z tego samego powodu.

W żadnej zdrowej rzeczywistości nie możesz argumentować, że ta praktyka nie robi nic, ale zaczyna od maksymalnej entropii.

Zrewidowałem cały ten bałagan i podkreśliłem wszystkie tautologie, a nowi zatrudnieni w college'ach byli znacznie bardziej produktywni, ponieważ nie musieli ścigać przez wiele poziomów pośrednich, na co constwskazywały te odniesienia, ponieważ nie byli wiarygodni w tym, jak zostali nazwani vs co zawierały.


źródło
112
Może powinieneś dodać kontrprzykład: przydałby const char DELIMITER = ':'się.
Bergi,
115
Przedstawiłbym kilka argumentów, które EMPTY_STRINGsą korzystne. (1) Mogę o wiele łatwiej znaleźć wszystkie zastosowania EMPTY_STRINGw pliku niż wszystkie "". (2) kiedy widzę EMPTY_STRING, wiem na pewno, że deweloper chciał, aby ten ciąg był pusty, i że nie jest to błędna edycja ani symbol zastępczy dla ciągu, który zostanie później dostarczony. Teraz twierdzisz, że przeze mnie argumentując, że możesz podszkolić moją wiedzę i bezpiecznie zignorować mnie na zawsze. Jak więc kwalifikujesz moją wiedzę? Czy planujesz na zawsze ignorować moją radę? Tak czy inaczej, nie mam problemu.
Eric Lippert,
39
@immibis: Możemy przestać myśleć o tych rzeczach jako użytecznych w kontekście zarządzania zmianami. Są stałymi. Nie zmieniają się. Pomyśl o nich jako użytecznych w kontekście ludzi poszukujących i rozumiejących semantykę kodu . Wiedza, że ​​coś jest separatorem pary klucz-wartość, jest znacznie bardziej użyteczna niż wiedza, że ​​jest to dwukropek; taki jest fakt na temat dziedziny semantycznej programu, a nie jego składni .
Eric Lippert,
15
@EricLippert: Jestem trochę punkt widzenia Tutaj, którzy podkreślają, że jedyną gwarancją, że constzapewnia to, że nie zmieni się przy starcie (po kompilacji), choć zgadzam się z tobą, że semantyczny sens z constjest znacznie ważniejsze niż jego wykorzystanie jako narzędzia zarządzania zmianami. To powiedziawszy, z pewnością mogę sobie wyobrazić coś, const EARLIEST_OS_SUPPORTEDco jest nie tylko semantycznie spójne, ale również zmieni się w czasie, gdy program ewoluuje i stare cruft jest usuwane.
Robert Harvey
16
@DanielJour: Więc to jest trzeci argument za EMPTY_STRING; że dobrze zaprojektowane IDE udostępni narzędzia, które pozwolą mi traktować ten byt symbolicznie, a nie syntaktycznie. Uogólnij to na czwarty argument: że biblioteka narzędzi do analizy kodu znajdująca się poniżej IDE może pozwolić na zaawansowaną programową analizę poprawności kodu na poziomie symbolicznym . Deweloper, który chce skorzystać z narzędzi bardziej zaawansowanych niż te napisane dosłownie 40 lat temu, musi jedynie wprowadzić niewielkie zmiany w swoich nawykach, aby czerpać korzyści z zaawansowanych narzędzi.
Eric Lippert,
145

Główną zaletą korzystania ze stałych jest to, że minimalizują konieczność konserwacji, gdy konieczna jest zmiana.

ABSOLUTNIE NIE. Nie jest to wcale powód do używania stałych, ponieważ stałe nie zmieniają się z definicji . Jeśli stała kiedykolwiek się zmienia, to nie była stała, prawda?

Odwoływanie się do używania stałych nie ma nic wspólnego z zarządzaniem zmianami i wszystko, co dotyczy uczynienia programów podatnymi na pisanie, rozumienie i utrzymywanie przez ludzi . Jeśli chcę wiedzieć wszędzie w moim programie, w którym dwukropek jest używany jako separator adresów URL, to mogę to wiedzieć bardzo łatwo, jeśli mam dyscyplinę, aby zdefiniować stały URLSeparator, i nie mogę tego tak łatwo wiedzieć, jeśli muszę grepować za :i zdobądź każde miejsce w kodzie, w którym :jest używane do wskazania klasy bazowej, ?:operatora lub cokolwiek innego.

Całkowicie nie zgadzam się z innymi odpowiedziami, które mówią, że jest to bezcelowa strata czasu. Nazwane stałe dodają znaczenia programowi, a semantyka może być wykorzystywana zarówno przez ludzi, jak i maszyny, do głębszego zrozumienia programu i utrzymania go bardziej efektywnie.

Sztuka polega na tym, aby nie unikać stałych, ale raczej nazywać je ich właściwościami semantycznymi, a nie ich właściwościami składniowymi . Do czego używana jest stała? Nie nazywaj go, Commachyba że domeną biznesową Twojego programu jest typografia, analiza języka angielskiego lub podobne. Nazwij to ListSeparatorczy coś takiego, aby wyjaśnić semantykę rzeczy.

Eric Lippert
źródło
42
Chociaż zgadzam się z duchem tego, co tu mówisz, twoje drugie / trzecie zdanie nie jest naprawdę poprawne. Stała może zmieniać się między wersjami pliku. W rzeczywistości większość programów, które piszę, mają stałą o nazwie coś podobnego MY_VER, która zawiera bieżący numer wersji programu, którego można następnie używać w pozostałej części programu zamiast magicznego ciągu znaków, takiego jak „5.03.427.0038”. Dodatkową korzyścią jest to, jak mówisz, że zawiera informacje semantyczne.
Monty Harder
50
Szczerze mówiąc, chodzi o to, że stała nie zmienia się podczas uruchamiania po zainicjowaniu, a nie, że nie zmienia się między kompilacjami. Z perspektywy kompilatora chodzi o to, że kompilator może przyjąć założenia, że ​​program nie jest w stanie go zmodyfikować; to, czy programista może go modyfikować podczas ponownej kompilacji, nie zmienia jego ciągłości. Mogą również wystąpić przypadki, w których oprogramowanie pobiera wartość sprzętową tylko do odczytu, na przykład poprzez dereferencję const volatile T*wskaźnika na określony adres; program nie może tego zmienić, ale sprzęt może.
Justin Time,
6
@MontyHarder: Dobra uwaga. Moja opinia jest oparta na fakcie, że zwykle używam języków, które rozróżniają stałe - które zawsze muszą pozostać niezmienne - i zmienne, które można przypisać raz - które mogą zmieniać się z wersji na wersję, uruchamiać, uruchamiać lub cokolwiek innego. Stała i zmienna to różne rzeczy; jeden pozostaje taki sam, a drugi zmienia się w czasie.
Eric Lippert,
7
@SteveCox: Zgadzam się; sposób, w jaki C / C ++ charakteryzuje „const”, jest dziwny i ma ograniczone zastosowanie. Właściwość, której chcę stałych, polega na tym, że ich wartości się nie zmieniają, nie że nie mam możliwości ich zmiany w niektórych funkcjach, ale nie w innych.
Eric Lippert,
15
„To wcale nie jest powód, aby używać stałych, ponieważ stałe nie zmieniają się z definicji. Jeśli stała kiedykolwiek się zmienia, to nie była stała, prawda?” Zmiana stałych w czasie kompilacji (oczywiście nie w czasie wykonywania) jest całkowicie normalna. Właśnie dlatego stworzyliście z nich wyraźnie „rzecz”. Oczywiście, stałe PO są śmieci, ale myśleć o czymś takim const VERSION='3.1.2'lub const KEYSIZE=1024czy cokolwiek innego.
AnoE
61

Nie, to głupie.

Co jest nie koniecznie głupi ciągnie takie rzeczy w nazwach etykiet ze względów lokalizacyjnych. Na przykład separator tysięcy to przecinek w Ameryce (1 000 000), ale nie przecinek w innych lokalizacjach. Przeciągnięcie tego do nazwanej etykiety (z odpowiednią nazwą bez przecinka) pozwala programiście zignorować / wyodrębnić te szczegóły.

Ale tworzenie stałej, ponieważ „magiczne łańcuchy są złe”, to po prostu kultywacja ładunków.

Telastyn
źródło
8
Lokalizacja jest zwykle bardziej skomplikowana niż zwykłe ciągi znaków. Na przykład niektóre języki chcą ogranicznika listy między wszystkimi elementami listy, podczas gdy inne wykluczają ogranicznik przed ostatnim elementem. Tak więc zwykle nie potrzeba zlokalizowanych stałych, ale zlokalizowanych reguł .
Vlad
19
W rzeczywistości separator tysięcy niekoniecznie jest separatorem tysięcy w innych lokalizacjach (Chiny / Japonia). Nie jest nawet ustawiony po stałej liczbie cyfr (Indie). Och, i mogą istnieć różne separatory w zależności od tego, czy jest to separator 1000, czy separator 1000000 (Meksyk). Ale to mniejszy problem niż brak używania cyfr ASCII 0–9 w niektórych lokalizacjach (Farsi). ux.stackexchange.com/questions/23667/…
Peter
1
@Vlad Lokalizacja jest znacznie bardziej skomplikowana, jednak separator tysięcy jest dobrze znanym przykładem, który ludzie rozpoznają.
To zależy od strategii lokalizacji ... czy zmieniasz wszystkie stałe w programie, aby je przetłumaczyć? A może powinieneś raczej odczytywać wartości z pliku (lub innego magazynu danych), aby efektywnie były to zmienne środowiska wykonawczego?
Paŭlo Ebermann
W takim razie nie byłoby to w ogóle przydatne. Program musiałby zostać ponownie skompilowany dla lokalizacji, co jest okropną praktyką. Powinny to być zmienne ładowane z plików definicji i wyszukiwane w razie potrzeby. Nie dlatego, że nie zgadzam się z tym punktem (głosowałem za odpowiedzią w górę), ale zająłbym trudniejsze stanowisko w tej sprawie.
29

Istnieje kilka znaków, które mogą być niejednoznaczne lub są używane do różnych celów. Na przykład używamy '-'jako łącznika, znaku minus, a nawet myślnika. Możesz utworzyć osobne nazwy jako:

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '-';
static const wchar_t EM_DASH = '-';

Później możesz zmodyfikować kod, aby go ujednoznacznić, zmieniając go jako:

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '\u2122';
static const wchar_t EM_DASH = '\u2014';

To może być powód, dla którego warto rozważyć zdefiniowanie stałych dla niektórych pojedynczych znaków. Jednak liczba niejednoznacznych znaków w ten sposób jest niewielka. Co najwyżej wydaje się, że zrobiłbyś to tylko dla nich. Argumentowałbym również, że możesz poczekać, aż rzeczywiście będziesz musiał rozróżnić dwuznaczne znaki, zanim uwzględnisz kod w ten sposób.

Ponieważ konwencje typograficzne mogą się różnić w zależności od języka i regionu, prawdopodobnie lepiej jest ładować tak niejednoznaczne znaki interpunkcyjne z tabeli tłumaczeń.

Adrian McCarthy
źródło
Dla mnie jest to jedyny uzasadniony powód, dla którego można tworzyć stałe znaków
FP
2
Używanie -jako kreski em jest dość mylące ... w większości czcionek jest to za krótkie. (Jest nawet krótszy niż myślnik.)
Paŭlo Ebermann
OK, nie najlepszy przykład. Zacząłem od strings, a wchar_tużywałem standardowej konwencji manuskryptu "--"dla kreski. Ale oryginalny przykład używał pojedynczych znaków, więc przełączyłem się, by pozostać wiernym temu pytaniu. Są ludzie, którzy -piszą po myślnikach, szczególnie podczas pracy z czcionką o stałej wysokości.
Adrian McCarthy
1
@ PaŭloEbermann Nie, tradycyjnie myślnik to szerokość znaku „m” kroju pisma, a myślnik to szerokość znaku „n”.
Dizzley,
@Dizzley tak i łącznik-szerokość <n-szerokość <m-szerokość.
Paŭlo Ebermann
22

Stała musi dodawać znaczenie.

Zdefiniowanie COMMA jako przecinka nie dodaje znaczenia, ponieważ wiemy, że przecinek jest przecinkiem. Zamiast tego niszczymy znaczenie, ponieważ teraz COMMA może już nie być przecinkiem.

Jeśli używasz przecinka do określonego celu i chcesz użyć nazwanej stałej, nazwij ją po jej celu. Przykład:

  • city + CharacterClass.COMMA + state = źle
  • city + CITY_STATE_DELIMITER + state = dobrze

Użyj funkcji do formatowania

Osobiście wolę FormatCityState(city, state)i nie dbam o to, jak wygląda ciało tej funkcji, o ile jest krótkie i przechodzi testy.

Piotr
źródło
1
Ach, ale przecinek nie zawsze jest tym samym przecinkiem. Mógłbym zdefiniować COMMA = „\ u0559” lub „\ u060C” itp. (Patrz Unicode), a nawet przekształcić go później w zmienną i odczytać z pliku konfiguracyjnego. W ten sposób będzie nadal miał to samo znaczenie , ale tylko inną wartość. Co ty na to.
Pan Lister,
2
@MrLister: YAGNI. Jeśli masz taką potrzebę: świetnie! Masz dobre rozwiązanie. Ale jeśli tego nie zrobisz - nie zaśmiecaj kodu, ponieważ być może kiedyś może. Z mojego doświadczenia wynika również, że jeśli próbujesz wprowadzić abstrakcje bez żadnej funkcji w bazie kodu, ludzie nie są dobrzy w konsekwentności. Tak więc, nawet jeśli zdefiniowałeś COMMA z zamiarem użycia innego punktu kodowego, w programie o wystarczającej wielkości i wieku, aby wybór w ogóle miał znaczenie, prawdopodobnie okaże się, że stała nie była używana wszędzie tam, gdzie powinna była były (i odwrotnie, mogły być również używane niewłaściwie).
Eamon Nerbonne,
17

Pomysł, że stała COMMA jest lepsza ','lub ","raczej łatwa do obalenia. Jasne, są przypadki, w których ma to sens, na przykład final String QUOTE = "\"";znaczne obniżenie czytelności bez wszystkich ukośników, ale z wyłączeniem znaków kontrolujących język, takich jak \ 'i ", nie uważałem ich za bardzo przydatne.

Używanie final String COMMA = ","jest nie tylko złą formą, jest niebezpieczne! Gdy ktoś chce zmienić separator z ","na ";", może zmienić plik stałych na, COMMA = ";"ponieważ jest to dla niego szybsze i po prostu działa. Poza tym, wiesz, wszystkie inne rzeczy, które teraz używały COMMA, to również średniki, w tym rzeczy wysyłane do zewnętrznych klientów. Tak więc przechodzi wszystkie twoje testy (ponieważ cały kod zestawiania i rozpakowywania również korzystał z COMMA), ale testy zewnętrzne zakończą się niepowodzeniem.

Przydatne jest nadanie im przydatnych nazw. I tak, czasami wiele stałych będzie miało tę samą zawartość, ale różne nazwy. Na przykład final String LIST_SEPARATOR = ",".

Więc twoje pytanie brzmi: „są stałymi pojedynczymi znakami lepsze niż literały”, a odpowiedź jednoznacznie brzmi „nie”. Ale nawet lepsza od obu tych nazw jest wąsko zakrojona nazwa zmiennej, która wyraźnie określa jej cel. Pewnie, wydasz kilka dodatkowych bajtów na te dodatkowe referencje (zakładając, że nie zostaną one skompilowane, co zapewne zrobią), ale w ramach długoterminowej konserwacji, czyli tam, gdzie jest większość kosztów aplikacji, są warte czasu.

corsiKa
źródło
Co powiesz na warunkowe zdefiniowanie DISP_APOSTROPHE jako pojedynczego cudzysłowu ASCII 0x27 lub Unicode (który jest bardziej typograficznym interpretacją apostrofu), w zależności od platformy docelowej?
supercat
3
faktycznie QUOTEprzykład dowodzi, że jest to zły pomysł, a ponieważ jesteś przypisując go do tego, co jest ogólnie / popularnie znany jako DOUBLE QUOTEi QUOTEsugeruje SINGLE_QUOTEco jest bardziej poprawnie dalej APOSTROPHE.
3
@JarrodRoberson Nie wydaje mi się, żeby cytat osobiście zawierał pojedynczy cytat - ale to kolejny dobry powód, aby usunąć niejasności tam, gdzie to możliwe!
corsiKa
2
Nie podoba mi się ten QUOTEprzykład z dodatkowego powodu - sprawia, że ​​skonstruowane z nim ciągi do czytania są jeszcze trudniejsze. "Hello, my name is " + QUOTE + "My Name" + QUOTEJest to trywialny przykład, a mimo to wygląda źle. Och, jasne, zamiast konkatenacji możesz użyć tokenów zastępujących, "Hello, my name is %sMy Name%s".format(QUOTE, QUOTE)może być tylko gorzej. Ale, hej, spróbujmy zaindeksowanych tokenów "Hello, my name is {0}My Name{0}".format(QUOTE)ugh, nie tak dużo lepiej. Każdy nietrywialny ciąg wygenerowany z cytatami byłby jeszcze gorszy.
VLAZ
2
@corsiKa - Będę żyć z unikanymi cytatami. Jeśli przegapię ucieczkę, IDE, którego używam, natychmiast narzeka. Kod najprawdopodobniej też się nie skompiluje. Jest dość łatwy do wykrycia. Jak łatwo jest popełnić błąd "My name is" + QUOTE + "My Name" + QUOTE, popełniając ten sam błąd trzy razy pisząc powyższy komentarz. Czy potrafisz to dostrzec? Jeśli trwa to Ci trochę, że to miejsce po brakującym jest . Czy formatujesz ciąg? W takim przypadku ciąg z wieloma tokenami do zastąpienia będzie jeszcze gorszy do opracowania. Jak mam go używać, aby był bardziej czytelny?
VLAZ
3

Zrobiłem trochę pracy, pisząc leksery i parsery i użyłem stałych całkowitych do reprezentowania terminali. Dla uproszczenia terminale jednoznakowe miały kod ASCII jako wartość liczbową, ale kod mógł być czymś zupełnie innym. Tak więc miałbym T_COMMA, któremu przypisano kod ASCII dla „,” jako jego stałej wartości. Jednak istniały również stałe dla nieterminali, którym przypisano liczby całkowite powyżej zestawu ASCII. Patrząc na generatory parserów, takie jak yacc lub bizon, lub parsery napisane przy użyciu tych narzędzi, mam wrażenie, że wszyscy tak robią.

Tak więc, podobnie jak wszyscy, uważam, że nie ma sensu definiować stałych w celu wyraźnego użycia stałych zamiast literałów w całym kodzie, ale wydaje mi się, że istnieją przypadki brzegowe (parsery, powiedzmy), w których można napotkać kod pełen zagadek stałe takie, jak opisujesz. Zauważ, że w przypadku parsera stałe nie służą tylko do przedstawienia literałów znaków; stanowią one podmioty, które mogą po prostu zdarzają się literały znakowe.

Mogę wymyślić kilka bardziej izolowanych przypadków, w których sensowne byłoby użycie stałych zamiast odpowiadających literałów. Na przykład możesz zdefiniować NEWLINE jako dosłowny „\ n” na polu unix, ale „\ r \ n” lub „\ n \ r”, jeśli korzystasz z systemu Windows lub Mac. To samo dotyczy parsowania plików reprezentujących dane tabelaryczne; możesz zdefiniować stałe FIELDSEPARATOR i RECORDSEPARATOR. W takich przypadkach faktycznie definiujesz stałą reprezentującą znak, który pełni określoną funkcję. Mimo to, jeśli jesteś początkującym programistą, może nazwałbyś swoją stałą separatora pól COMMA, nie zdając sobie sprawy, że powinieneś nazwać to FIELDSEPARATOR, a zanim się zorientujesz, kod będzie w produkcji i przejdziesz do następnego projekt,

Wreszcie opisana praktyka może mieć sens w kilku przypadkach, w których piszesz kod do obsługi danych zakodowanych w określonym kodowaniu znaków, powiedzmy iso-8859-1, ale spodziewaj się, że kodowanie zmieni się później. Oczywiście w takim przypadku rozsądniej byłoby użyć do tego celu bibliotek lokalizacyjnych lub kodujących i dekodujących, ale jeśli z jakiegoś powodu nie można użyć takiej biblioteki do obsługi problemów z kodowaniem, używając tylko stałych trzeba przedefiniować w jednym pliku zamiast literałów na stałe zaśmieconych w całym kodzie źródłowym.

Jeśli chodzi o artykuł, do którego linkujesz: nie sądzę, aby próbował uzasadnić zastąpienie literałów znaków stałymi. Myślę, że próbuje zilustrować metodę użycia interfejsów do pobierania stałych do innych części bazy kodu. Przykładowe stałe użyte do zilustrowania tego są wybierane bardzo źle, ale nie sądzę, aby miały one jakikolwiek wpływ.

Pascal
źródło
2
Myślę, że próbuje zilustrować metodę użycia interfejsów do pobierania stałych do innych części bazy kodu. co jest jeszcze gorszym anty-wzorem i jest również ściśle powiązane i ma niską kohezję, nie ma też uzasadnionego powodu, aby to robić.
3

Oprócz wszystkich dobrych odpowiedzi tutaj, chciałbym dodać do myślenia, że ​​dobre programowanie polega na zapewnieniu odpowiednich abstrakcji, które mogą być budowane przez ciebie i może innych, bez konieczności powtarzania tego samego kodu w kółko.

Dobre abstrakcje sprawiają, że kod jest łatwy w użyciu z jednej strony i łatwy w utrzymaniu z drugiej.

Całkowicie zgadzam się, że DELIMITER=':'samo w sobie jest słabą abstrakcją i tylko lepszą niż COLON=':'(ponieważ ta ostatnia jest całkowicie zubożała).

Dobra abstrakcja obejmująca ciągi znaków i separatory obejmowałaby sposób na spakowanie jednego lub więcej pojedynczych elementów treści w łańcuch i rozpakowanie ich również z zapakowanego łańcucha, przede wszystkim, zanim powiesz, czym jest separator. Taka abstrakcja byłaby powiązana jako koncepcja, w większości języków jako klasa; na przykład, aby jego użycie było praktycznie samo dokumentujące, ponieważ można wyszukiwać wszystkie miejsca, w których ta klasa jest używana i mieć pewność co do zamiarów programisty dotyczących formatu spakowanych ciągów w każdym przypadku, w którym użyto pewnej abstrakcji.

Po dostarczeniu takiej abstrakcji byłoby łatwe do użycia bez konieczności sprawdzania wartości DELIMITERlub COLON, a zmiana szczegółów implementacji byłaby zasadniczo ograniczona do implementacji. Krótko mówiąc, te stałe powinny naprawdę być szczegółami implementacji ukrytymi w odpowiedniej abstrakcji.

Główną zaletą korzystania ze stałych jest to, że minimalizują konieczność konserwacji, gdy konieczna jest zmiana.

Dobre abstrakty, które są zazwyczaj kompozycjami kilku powiązanych ze sobą możliwości, lepiej minimalizują konserwację. Po pierwsze, wyraźnie oddzielają dostawcę od konsumentów. Po drugie, ukrywają szczegóły implementacji i zapewniają bezpośrednio użyteczną funkcjonalność. Po trzecie, dokumentują na wysokim poziomie, kiedy i gdzie są używane.

Erik Eidt
źródło
2

Kiedyś widziałem, że takie stałe są skutecznie wykorzystywane, to dopasowanie istniejącego API lub dokumentu. Widziałem takie symbole, jak COMMAużywane, ponieważ określony program był bezpośrednio podłączony do analizatora składni, który był używany COMMAjako znacznik w abstrakcyjnym drzewie składni. Widziałem też, jak kiedyś pasowało to do formalnej specyfikacji. w formalnych specyfikacjach czasem zobaczysz symbole takie jak COMMA, ','ponieważ nie chcą być tak jasne, jak to możliwe.

W obu przypadkach użycie nazwanego symbolu, takiego jak, COMMApomaga zapewnić spójność rozłącznego produktu. Wartość ta często przewyższa koszt nadmiernie szczegółowych notacji.

Cort Ammon
źródło
2

Zauważ, że próbujesz utworzyć listę.

Przeanalizuj to jako: String makeList(String[] items)

Innymi słowy, rozłóż logikę zamiast danych .
Języki mogą różnić się sposobem reprezentowania list, ale przecinki są zawsze przecinkami (to tautologia). Jeśli więc zmieni się język, zmiana znaku przecinka nie pomoże - ale tak będzie.

Mehrdad
źródło
0

Jeśli była to klasa napisana jako część aplikacji przez innego programistę, prawie na pewno jest to zły pomysł. Jak już zauważyli inni, sensowne jest definiowanie stałych, takich jak SEPARATOR = ','miejsce zmiany wartości, a stała ma sens, ale tym bardziej stałe, których nazwa opisuje tylko ich wartość.

Istnieją jednak co najmniej dwa przypadki, w których sensowne jest zadeklarowanie stałych, których nazwa dokładnie opisuje ich zawartość i w których nie można zmienić wartości bez odpowiedniej zmiany nazwy stałej:

  • Stałe matematyczne lub fizyczne, np PI = 3.14159. Tutaj rolą stałej jest działanie jako mnemoniczne, ponieważ nazwa symboliczna PIjest znacznie krótsza i bardziej czytelna niż reprezentowana przez nią wartość.
  • Wyczerpujące listy symboli w parserze lub klawisze na klawiaturze. Może nawet mieć sens lista stałych zawierająca większość lub wszystkie znaki Unicode i tutaj może się zdarzyć twoja sprawa. Niektóre postacie Asą oczywiste i wyraźnie rozpoznawalne. Ale można łatwo powiedzieć Аi Aod siebie? Pierwszym z nich jest cyrylica list А a drugi jest łacińska litera . Są to różne litery reprezentowane przez różne punkty kodu Unicode, chociaż graficznie są prawie identyczne. Wolałbym mieć stałe CYRILLIC_CAPITAL_AiLATIN_CAPITAL_Aw moim kodzie niż dwa prawie identycznie wyglądające znaki. Oczywiście nie ma to sensu, jeśli wiesz, że będziesz pracować tylko ze znakami ASCII, które nie zawierają cyrylicy. Podobnie: używam alfabetu łacińskiego na co dzień, więc gdybym pisał program, który potrzebował chińskiego znaku, prawdopodobnie wolałbym używać stałej zamiast wstawiać znak, którego nie rozumiem. Dla kogoś, kto używa chińskich znaków na co dzień, chiński znak może być oczywisty, ale łaciński może być łatwiejszy do przedstawienia jako nazwana stała. Jak widać, zależy to od kontekstu. Mimo to biblioteka może zawierać stałe symboliczne dla wszystkich znaków, ponieważ autorzy nie mogą z góry wiedzieć, w jaki sposób biblioteka będzie używana i które znaki mogą potrzebować stałych, aby poprawić czytelność w konkretnej aplikacji.

Jednak takie przypadki są zwykle obsługiwane przez klasy systemowe lub biblioteki specjalnego przeznaczenia, a ich występowanie w kodzie napisanym przez twórców aplikacji powinno być bardzo rzadkie, chyba że pracujesz nad jakimś bardzo specjalnym projektem.

Michał Kosmulski
źródło
-1

Może.

Stałe pojedynczych znaków są stosunkowo trudne do rozróżnienia. Dlatego łatwo jest przeoczyć fakt, że dodajesz kropkę zamiast przecinka

city + '.' + state

podczas gdy jest to stosunkowo trudny błąd

city + Const.PERIOD + state

W zależności od środowiska internacjonalizacji i globalizacji różnica między apostrofem ASCII a apostrofem otwartym i zamkniętym Windows-1252 (lub podwójnym cytatem ASCII i podwójnym cudzysłowem Windows-1252) może być znacząca i niezwykle trudno jest wizualizować wygląd na kod.

Teraz, przypuszczalnie, jeśli błędne wstawienie kropki zamiast przecinka było znaczącym problemem funkcjonalnym, miałbyś automatyczny test, który znalazłby literówkę. Jeśli twoje oprogramowanie generuje pliki CSV, spodziewam się, że Twój zestaw testów dość szybko odkryje, że miałeś okres między miastem a stanem. Jeśli twoje oprogramowanie ma działać dla klientów z różnymi konfiguracjami internacjonalizacji, prawdopodobnie twój zestaw testowy będzie działał w każdym środowisku i odbierze, jeśli masz otwartą ofertę Microsoft, jeśli chciałbyś mieć apostrof.

Mogę sobie wyobrazić projekt, w którym sensowniejsze byłoby wybranie bardziej pełnego kodu, który mógłby rozwiązać te problemy, szczególnie gdy masz starszy kod, który nie ma kompleksowego zestawu testów, chociaż prawdopodobnie nie kodowałbym w ten sposób projekt rozwoju zielonego pola. Dodanie stałej dla każdego znaku interpunkcyjnego, a nie tylko tych, które są potencjalnie problematyczne w konkretnej aplikacji, jest prawdopodobnie rażącą przesadą.

Justin Cave
źródło
2
co się stanie, gdy jakiś kretyn zmieni się Const.PERIODna równy ~? Tautologia nazwanych postaci nie ma uzasadnienia, po prostu dodaje konserwacji i złożoności, której nie trzeba we współczesnych środowiskach programistycznych. Czy zamierzasz napisać zestaw testów jednostkowych, które w zasadzie mówią assert(Const.PERIOD == '.')?
3
@JarrodRoberson - To by było do kitu, jasne. Ale miałbyś tyle samo kłopotów, gdyby ktoś dodał stałą Unicode, która wygląda prawie dokładnie jak przecinek, a nie przecinek. Tak jak powiedziałem, nie jest to coś, co zrobiłbym w projekcie rozwoju od podstaw. Ale jeśli masz starszą bazę kodu z wypryskowym pakietem testowym, w którym potknąłeś się przecinkiem / kropką lub apostrofem / obrzydliwością apostrofy Microsoft wydaje kilka razy, tworzenie stałych i mówienie ludziom, aby z nich korzystali, może być rozsądnym sposobem na zrobienie tego kod lepiej bez spędzania roku na pisaniu testów.
Justin Cave,
3
Twój stary przykład jest kiepski, właśnie skończyłem refaktoryzację bazy kodów LOC ponad 1 000 000, która ma 18 lat. Miał każdy znak do wydrukowania tak zdefiniowany wiele razy, nawet z różnymi sprzecznymi nazwami. I wiele razy rzeczy nazwane COMMAbyły ustawione = SPACE + "," + SPACE. Tak, jakiś idiota miał SPACEstałą. Przebudowałem je WSZYSTKO, a kod był o wiele bardziej czytelny, a pracownicy college'u byli znacznie bardziej zdolni do wytropienia rzeczy i naprawienia ich bez 6 poziomów pośrednich, aby dowiedzieć się, co właściwie było ustawione.
-1

Czy stałe jednoznakowe są lepsze od literałów?

Wokół pływa wiele konflacji. Zobaczmy, czy potrafię drażnić ich osobno.

Stałe zapewniają:

  • semantyka
  • zmiana podczas rozwoju
  • pośrednictwo

Zejście do nazwy pojedynczego znaku wpływa tylko na semantykę. Nazwa powinna być przydatna jako komentarz i czytelna w kontekście. Powinien wyrażać znaczenie, a nie wartość. Jeśli da radę, wystarczy jedna grzywna. Jeśli nie może, proszę nie.

Dosłowność i stała mogą się zmieniać zarówno podczas rozwoju. To właśnie powoduje problem z magiczną liczbą. Ciągi mogą być również liczbami magicznymi.

Jeśli istnieje znaczenie semantyczne, a ponieważ oba są stałe, to to, czy stała ma większą wartość niż literał, sprowadza się do pośrednictwa.

Pośrednictwo może rozwiązać każdy problem inny niż za dużo pośrednie.

Pośrednictwo może rozwiązać problem magicznej liczby, ponieważ pozwala wybrać wartość pomysłu w jednym miejscu. Semantycznie, aby było warto, nazwa musi wyjaśnić, co ten pomysł jest jasny. Nazwa powinna dotyczyć pomysłu, a nie wartości.

Pośrednictwo może zostać przesadzone. Niektórzy wolą wyszukiwać i zamieniać literały, aby wprowadzić zmiany. To dobrze, o ile 42 jest wyraźnie sensem życia i nie jest mieszane z 42, liczbą atomową molibdenu.

To, gdzie możesz zrobić użyteczne rozróżnienie za pomocą jednej litery, zależy w dużej mierze od kontekstu. Ale nie zrobiłbym z tego nawyku.

candied_orange
źródło
1
Semantyczny jest kluczem. Jeśli i „A” ma więcej semantycznego niż po prostu „A”, to warto powiązać ten sam semantyczny z tym samym „odniesieniem”. Nie ma znaczenia, czy jest to stałe, czy nie. W pełni się zgadzam.
oopexpert
-1

Jako filozoficzny przeciwieństwo opinii większości muszę stwierdzić, że niektórzy z nas doceniają niewyszukanego XIX-wiecznego francuskiego programistę i chłopa

przypomniał sobie swoją monotonną, wieczną jasność, jego oszałamiająco rozsądne poglądy na wszystko, jego kolosalne zadowolenie z truizmów tylko dlatego, że były prawdziwe. „Pomieszaj to wszystko!” - zawołał Turnbull do siebie - „jeśli jest w azylu, nie może być nikogo na zewnątrz”.

GK Chesterton, The Ball and The Cross

Nie ma nic złego w docenieniu prawdy i nie ma nic złego w mówieniu prawdy, szczególnie podczas rozmowy z komputerem.

Jeśli okłamiesz komputer, dostaniesz go

Perry Farrar - Germantown, Maryland (z More Perły programistyczne)


Ale w większości zgadzam się z ludźmi, którzy mówią, że to głupie. Jestem zbyt młody, aby nauczyć się programować FORTRAN, ale słyszałem, że można na nowo zdefiniować 'A' = 'Q'i wymyślić wszelkiego rodzaju wspaniałe kryptogramy. Nie robisz tego.

Poza poruszonymi wcześniej problemami z i18n (które nie redefiniują glifu „COMMA”, ale naprawdę redefiniują glif DECIMAL_POINT). Konstruowanie francuskich cytatów z marchwi lub pojedynczych cytatów z Wielkiej Brytanii w celu przekazania ludziom znaczenia ma sens i te naprawdę powinny być zmiennymi, a nie stałymi. Stała byłaby AMERICAN_COMMA := ','icomma := AMERICAN_COMMA

I gdybym używał wzorca konstruktora do skonstruowania zapytania SQL, wolałbym raczej zobaczyć

sb.append("insert into ")
 .append(table_name)
 .append(" values ")
 .append(" ( ")
 .append(val_1)
 .append(",")
 .append(val_2)
 .append(" ); ")

niż cokolwiek innego, ale gdybyś chciał dodać stałe, byłoby to możliwe

INSERT_VALUES_START = " ( "
INSERT_VALUES_END = " ) "
INSERT_VALUES_SEPARATOR = " , "
QUERY_TERMINATOR = ";"

sb.append("insert into ")
 .append(table_name)
 .append(" values ")
 .append(INSERT_VALUES_START)
 .append(val_1)
 .append(INSERT_VALUES_SEPARATOR)
 .append(val_2)
 .append(INSERT_VALUES_END)
 .append(QUERY_TERMINATOR)

Jeśli jednak oglądałeś kiedyś program (lub typ), możesz zauważyć ciekawe dziwactwa. Nie wszyscy jesteśmy gwiezdnymi maszynistkami. Wielu z nas spóźniło się na programowanie lub wychowało się na sowieckich klawiaturach (gdzie klawisze są pisane na tobie) i lubimy wycinać i wklejać poszczególne litery zamiast próbować je znaleźć na klawiaturze i / lub polegać na autouzupełnianiu.

Nic nie będzie dla ciebie autouzupełniania ciągu, więc jeśli mogę dostać przecinek, naciskając „con”, alt-space, dół, dół, dół, wejdź i uzyskaj wycenę, naciskając „con”, alt-space, dół, w dół, wprowadź. Mogę to po prostu zrobić.


Kolejną rzeczą, o której należy pamiętać o literałach łańcuchowych, jest sposób ich kompilacji. Przynajmniej w Delphi (który jest jedynym językiem, którego obsesję na punkcie stosu) skończysz dosłownie na stosie każdej funkcji. Tak więc dużo literałów = dużo narzutu funkcji; „,” w funkcji_A nie jest tym samym bitem pamięci co „,” w funkcji_B. Aby temu przeciwdziałać, istnieje „ciąg zasobów”, który można budować i łączyć z boku - i tak robią i18n rzeczy (zabijanie) dwa ptaki z jednym krzakiem) .W Pythonie wszystkie twoje dosłowne ciągi znaków są obiektami i może to wydawać się przyjemne w użyciu utils.constants.COMMA.join(["some","happy","array","strings"]), ale nie jest to świetny pomysł na punkty powtarzane w kółko na tej stronie.

Peter Turner
źródło
-4

Ale kiedy zaczniemy używać innego symbolu niż „,” do przedstawienia przecinka?

Do lokalizacji.

W krajach anglojęzycznych symbolem oddzielającym całą i ułamkową część dziesiętną jest „.”, Którą nazywamy „kropką dziesiętną”. W wielu innych krajach symbolem jest „,” i zwykle jest nazywany odpowiednikiem „przecinka” w lokalnym języku. Podobnie, gdy kraje anglojęzyczne używają „,” do oddzielania grup po trzy cyfry dużymi liczbami (np. 1 000 000 na milion), kraje, które używają przecinka jako przecinka dziesiętnego, używają kropki (1 000 000).

Tak więc istnieje potrzeba tworzenia stałych DECIMAL_POINT i COMMA, jeśli robisz globalizację.

Paul G.
źródło
2
Ale wtedy COMMA i DECIMAL_POINT nie są poprawnymi nazwami bytów (prawdopodobnie dlatego zostałeś odrzucony).
Kyle Strand
Musisz skompilować określone zlokalizowane wersje. Stałe dosłowne nie są do tego odpowiednie; ten przypadek użycia wywoływałby w nich pliki definicji i wyszukiwania (które mogą obejmować stałe, ale stałe wyszukiwania, a nie stałe znaki).