Czy to użycie symbolicznej stałej nadwyżki umiejętności?

34

Jestem dość nowy w inżynierii oprogramowania, dlatego jako ćwiczenie napisałem grę w szachy. Mój przyjaciel rzucił na to okiem i zwrócił uwagę, że mój kod wygląda

for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){

podczas gdy nalegał, aby tak było

for (int i = 0; i < CHESS_CONST; i++){
    for (int j = 0; j < CHESS_CONST; j++){

z jakąś lepszą nazwą symbolu, o której nie mogę teraz myśleć.

Teraz oczywiście wiem, że na ogół unikam używania magicznych liczb, ale od tego czasu mam na to ochotę

  1. liczba ta nigdy się nie zmieni;
  2. i tak nazwa nie może być tak opisowa, ponieważ numer jest używany w tak wielu miejscach w całym kodzie; i
  3. każdy, kto przechodzi przez kod źródłowy programu szachowego, powinien wiedzieć wystarczająco dużo o szachach, aby wiedzieć, do czego służy 8,

tak naprawdę nie ma potrzeby stałej symbolicznej.

Więc co myślicie? Czy to przesada, czy powinienem pójść zgodnie z konwencją i użyć symbolu?

chocolatedevil
źródło
46
CHESS_CONST jest o wiele gorszy niż zwykłe używanie liczby 8, ale poprawa mogłaby być stała o opisowej nazwie. Mówisz, że każdy powinien wiedzieć, co oznacza 8 w kodzie, ale to nie jest prawda. Liczba całkowita bez kontekstu może oznaczać dowolną liczbę rzeczy, na przykład liczbę ruchów, liczbę elementów na planszy i tak dalej. Opisowa nazwa stałej wyjaśniłaby intencję i dlatego kod byłby łatwiejszy do zrozumienia.
JacquesB,
43
Osobiście bardziej niepokojące niż magiczna liczba są nazwy ii jzmienne pętli. Nie mogę z mojego życia ustalić, który z nich ma reprezentować rangę, a który ma reprezentować plik. Szeregi zakresie od 1..8 oraz pliki wahają się od a..h, ale w twoim przypadku, zarówno ii jzakres od 0..7, tak że nie pomoże mi zobaczyć co jest czym. Czy istnieje jakiś kryzys związany z brakiem listów międzynarodowych, o którym nie wiem, lub co jest złego w zmianie nazwy na ranki file?
Jörg W Mittag
4
Graj adwokata diabła przez sekundę; Wyobraź sobie, że czytasz czyjś kod szachowy, w którym użył magicznej liczby 8. Czy możesz założyć, że wiesz, do czego służy? Jak możesz być w 100% pewien? Czy jest jakaś możliwość, że może to oznaczać coś innego? Czy nie byłoby to trochę przyjemniejsze, gdybyś nawet nie musiał zakładać? Ile czasu możesz poświęcić na śledzenie kodu, aby dowiedzieć się, czy twoje założenie jest słuszne? Czy spędziłbyś mniej czasu, gdyby kod był bardziej samodokumentujący, używając zamiast tego sensownej, wnikliwej nazwy?
Ben Cottrell,
16
@JacquesB: Rzeczywiście. Jest 8 rang, 8 plików, 8 pionków. Niektóre silniki szachowe używają systemu punktów, w którym pewne elementy, niektóre pola i pewne ruchy mają przypisane punkty, a silnik wybiera ruch z najwyższym wynikiem. 8 może być takim wynikiem. 8 może być domyślną głębokością wyszukiwania. To może być prawie wszystko.
Jörg W Mittag
9
Rozważałbym użycie pętli foreach, np foreach(var rank in Ranks). Rozważę również połączenie obu pętli w jedną, w której każdy element jest krotką (ranga, plik).
CodesInChaos

Odpowiedzi:

101

IMHO twój przyjaciel ma rację używając nazwy symbolicznej, chociaż myślę, że nazwa powinna zdecydowanie być bardziej opisowa (jak BOARD_WIDTHzamiast CHESS_CONST).

Nawet jeśli liczba nigdy się nie zmieni w trakcie trwania programu, mogą istnieć inne miejsca w twoim programie, w których liczba 8 będzie miała inne znaczenie. Zastąpienie „8” BOARD_WIDTHwszędzie tam, gdzie oznacza się szerokość płytki, i użycie innej symbolicznej nazwy, gdy chodzi o inną rzecz, czyni te różne znaczenia wyraźnymi, oczywistymi, a cały program jest bardziej czytelny i łatwy do utrzymania. Umożliwia także globalne wyszukiwanie w programie (lub wyszukiwanie symboli odwrotnych, jeśli zapewnia to Twoje środowisko) na wypadek, gdybyś musiał szybko zidentyfikować wszystkie miejsca w kodzie, które zależą od szerokości płyty.

Zobacz także poprzedni post na SE.SE, aby dowiedzieć się, jak (lub jak nie) wybierać nazwy dla liczb.

Na marginesie, ponieważ zostało to omówione tutaj w komentarzach : jeśli w kodzie twojego prawdziwego programu ma znaczenie to, czy zmienna iodnosi się do wierszy i jkolumn tablicy lub odwrotnie, zaleca się wybranie nazw zmiennych, które wyraźne rozróżnienie, jak rowi col. Zaletą takich nazw jest to, że sprawiają, że zły kod wygląda źle.

Doktor Brown
źródło
22
Chciałbym dodać, że jeśli OP napisałby naprawdę świetny silnik szachowy, to chciałby go rozszerzyć o szachy 3D lub inne warianty, to które 8s trzeba zmienić, będzie wyzwaniem.
Steve Barnes,
32
@ SteveBarnes: Staram się unikać takich argumentów, ponieważ zbyt łatwo prowadzą one do odwróconego argumentu, takiego jak: „Myślę, że jest bardzo inaczej niż kiedykolwiek uczynić ten program inną grą, więc mogę zostawić magiczną liczbę 8 tam, gdzie ona jest. „
Doc Brown
4
@IllusiveBrian To klasyczny argument „słomianego człowieka” z dwóch powodów: (1) samo zdefiniowanie stałej nie oznacza, że ​​programista nadal nie może wpisać „8” (przypadkowo, projektowo lub złośliwie!) I (2) tylko dlatego, że istnieje stała BOARD_WIDTH = 8, nie czyni BOARD_WIDTH poprawną stałą do zastosowania w określonym miejscu w programie.
alephzero,
3
@Blrfl ... lub doprowadzi do nadmiernej inżynierii kodu. Obowiązkiem dewelopera jest uświadomienie sobie, co może się zmienić w przyszłości i odpowiednie kodowanie. Kodowanie wymagań nie zmieni się, powoduje problemy, ale druga skrajność nie jest lepsza.
Malcolm,
4
@corsiKa Gdy zmienne pętli odpowiadają osiom fizycznym, powinny być nazwane x, y lub row, col lub, w przypadku szachów, file, rank.
user949300
11

Ok, oto kilka komentarzy, które mam:

Pozbycie się magicznych liczb to świetny pomysł. Istnieje pojęcie znane jako DRY, które często jest fałszywie przedstawiane, ale chodzi o to, że nie powielasz znajomości pojęć w swoim projekcie. Więc jeśli masz klasę o nazwie ChessBoard, możesz zachować stałą o nazwie BOARD_SIZE lub ChessBoard.SIZE dołączoną do niej. W ten sposób istnieje jedno jedyne źródło tych informacji. Pomaga to również w czytelności później:

for (int i = 0; i < ChessBoard.SIZE; i++){
  for (int j = 0; j < ChessBoard.SIZE; j++){

Nawet jeśli liczba nigdy się nie zmienia, Twój program jest prawdopodobnie lepszy. Każda osoba, która go czyta, wie więcej informacji o tym, co robi kod.

Złe imię jest gorsze niż brak imienia, ale to nie znaczy, że czegoś nie należy nazywać. Po prostu zmień nazwę. Nie wylewaj dziecka z kąpielą. : p Nazwa może być opisowa, o ile dobrze rozumiesz, co ona opisuje. Następnie tę koncepcję można zastosować do wielu różnych rzeczy.

rozjaśnia
źródło
8
wydaje się, że to tylko powtórzenie punktów, które już zostały wyjaśnione i wyjaśnione w najwyższej odpowiedzi
komnata
-7

To, czego naprawdę chcesz, to wyeliminowanie odwołań do stałych reklam na stałe, bez względu na to, czy są nazwane, czy nie

for_each_chess_square (row, col) {
  /*...*/
}

Jeśli zamierzasz rozpowszechniać stałą, powtarzając takie pętle i tak dalej, najlepiej trzymać się tego 8.

8jest samoopisujący; to nie makro oznacza coś innego.

Nie zmienisz go nigdy w program szachowy 9x9, a jeśli kiedykolwiek to zrobisz, mnożenie 8 nie będzie główną trudnością.

Możemy przeszukać 150 000 baz kodu linii dla tokena 8 i sklasyfikować, które wystąpienia oznaczają co w sekundach.

O wiele ważniejsza jest modularyzacja kodu, aby wiedza szachowa była skoncentrowana w jak najmniejszej liczbie miejsc. Lepiej mieć jeden, dwa, może trzy moduły szachowe, w których występuje dosłownie 8, niż trzydzieści siedem modułów związanych z odpowiedzialnością szachową, odnosząc się do 8 poprzez symboliczną nazwę.

Kiedy lub jeśli ta 8 stała się źródłem napięcia w twoim programie, możesz to łatwo naprawić w tym czasie. Napraw prawdziwe problemy, które mają miejsce teraz. Jeśli nie czujesz, że przeszkadza ci ta konkretna 8, idź z tym instynktem.

Załóżmy, że w przyszłości chcesz obsługiwać alternatywne wymiary płyty. W takim przypadku pętle te będą musiały zmienić, czy używają nazwanej stałej, czy 8, ponieważ wymiary zostaną odczytane przez jakieś wyrażenie, takie jak board.widthi board.height. Jeśli masz BOARD_SIZEzamiast 8, te miejsca będą łatwiejsze do znalezienia. To mniej wysiłku. Nie należy jednak zapominać o wysiłku zastąpienia 8go BOARD_SIZE. Ogólny wysiłek nie jest niższy. Dokonywanie jednej przejść nad kodem do zmian 8do BOARD_SIZE, a potem jeszcze do wspierania alternatywnych wymiarów, nie jest tańsze niż po prostu się z 8alternatywnej wsparcia wymiaru.

Możemy też na to spojrzeć z czysto zimnej, obiektywnej analizy stosunku korzyści do ryzyka. Program ma teraz w sobie stałe. Jeśli zostaną one zastąpione stałą, nie ma korzyści; program jest identyczny. Przy każdej zmianie istnieje niezerowe ryzyko. W tym przypadku jest mały. Mimo to nie należy podejmować ryzyka bez korzyści. Aby „sprzedać” zmianę w obliczu tego rozumowania, musimy postawić hipotezę korzyści: przyszłej korzyści, która pomoże w innym programie: przyszłej wersji programu, która nie istnieje teraz. Jeśli planowany jest taki program, hipoteza i związane z nią rozumowanie są w dobrej wierze i należy je traktować poważnie.

Na przykład, jeśli dzieli Cię kilka dni od dodania więcej kodu, który jeszcze bardziej rozpowszechni te stałe, możesz z nich zrezygnować. Jeśli te instancje stałych to w przybliżeniu wszystkie instancje, które kiedykolwiek będą istnieć, to po co się tym przejmować.

Jeśli kiedykolwiek pracujesz nad oprogramowaniem komercyjnym, zastosowanie będą miały również argumenty dotyczące ROI. Jeśli program nie sprzedaje, a zamiana niektórych liczb zakodowanych na stałe nie poprawi sprzedaży, wysiłek nie zostanie zrekompensowany. Zmiana ma zerowy zwrot z inwestycji czasu. Argumenty dotyczące ROI generalizują się poza pieniądzem. Napisałeś program, inwestując czas i wysiłek, i coś z tego wyciągnąłeś: to twój zwrot, twoje „R”. Jeśli dokonując tej zmiany samodzielnie, uzyskasz więcej tego „R”, cokolwiek to jest, to za wszelką cenę. Jeśli masz jakiś plan dalszego rozwoju, a ta zmiana poprawi twoje „R”, to samo. Jeśli zmiana nie ma dla Ciebie natychmiastowego lub przewidywalnego „R”, zapomnij o tym.

Kaz
źródło
13
8NIE jest tak samoopisujące się, to liczba, która może znaczyć cokolwiek. A może chcą zagrać w szachy 9x9, dlaczego nie? Jeśli masz 150 000 wierszy kodu, nie ma możliwości zapamiętania znaczenia każdego z nich 8w kodzie, a nawet gdybyś mógł, dlaczego miałbyś to zrobić? To tylko wiele dodatkowych problemów. Jeśli chodzi o modularyzację, zastąpienie 8czegoś podobnego NUM_RANKSnie szkodzi modułowości, w rzeczywistości pomaga, ponieważ ułatwia manipulowanie kodem.
Jerfov2
4
@Kaz Też to robię. A potem wprowadzam znaczące dziwne błędy, ponieważ garść zastosowań nie była taka, jak myślałem, ponieważ ciężko jest zwracać tak dużą uwagę na dużą liczbę zamienników, a także być poprawnym za każdym razem. Z tego powodu unikam w ogóle wchodzenia w ten scenariusz, więc nie mogę poprzeć rad, które wyrażają na to zgodę.
doppelgreener
1
„Jednak nie należy zapominać o wysiłku zastępując 8ze BOARD_SIZEna pierwszym miejscu” - czas pan spędził kontrolowania nad kodzie próbując zrozumieć, co każdy 8robi w programie miesięcy od teraz najprawdopodobniej przekroczy czas spędzony wymiany 8z znacząca stała na poziomie modułu.
Christian Dean
1
@doppelganger Ten scenariusz już tu jest. Nie mówię, weź program szachowy, który używa stałych, i zamień je na 8. Nie mówię też: „nie planuj nie używać stałych w jeszcze nie napisanym programie szachowym”
Kaz
1
@Kaz Dlaczego chciałbyś zmusić się do przeczytania całego kodu wokół 8, a może czasem źle zrozumiałeś kontekst, gdy mógłbyś po prostu spędzić dodatkowe 3 sekundy, dając wymowne imię? Jeśli chodzi o znajomość rzeczywistej wartości stałej, o wiele łatwiej jest szukać dokładnej wartości zmiennej niż próbować
zrewidować,