Chciałbym dodać coś, na co wskazują inne odpowiedzi, ale nie sądzę, by zostało to wyraźnie wymienione:
@puck mówi: „Nadal nie ma gwarancji, że pierwszy wymieniony argument w nazwie funkcji jest naprawdę pierwszym parametrem”.
@cbojar mówi „Używaj typów zamiast niejednoznacznych argumentów”
Problem polega na tym, że języki programowania nie rozumieją nazw: są one po prostu traktowane jako nieprzezroczyste, atomowe symbole. Dlatego, podobnie jak w przypadku komentarzy do kodu, niekoniecznie istnieje korelacja między nazwą funkcji a jej faktycznym działaniem.
Porównaj assertExpectedEqualsActual(foo, bar)
z niektórymi alternatywami (z tej strony i gdzie indziej), takimi jak:
# Putting the arguments in a labelled structure
assertEquals({expected: foo, actual: bar})
# Using a keyword arguments language feature
assertEquals(expected=foo, actual=bar)
# Giving the arguments different types, forcing us to wrap them
assertEquals(Expected(foo), Actual(bar))
# Breaking the symmetry and attaching the code to one of the arguments
bar.Should().Be(foo)
Wszystkie mają większą strukturę niż pełna nazwa, co nadaje temu językowi coś nieprzejrzystego. Definicja i użycie funkcji zależy również od tej struktury, więc nie może ona zsynchronizować się z tym, co robi implementacja (jak nazwa lub komentarz może).
Kiedy napotykam lub widzę taki problem, zanim sfrustruję swój komputer, najpierw zastanawiam się, czy to „uczciwe” obwinianie maszyny. Innymi słowy, czy maszyna otrzymała wystarczającą ilość informacji, aby odróżnić to, czego chciałam od tego, o co prosiłam?
Takie wezwanie assertEqual(expected, actual)
ma tak samo sens, jak assertEqual(actual, expected)
, więc łatwo jest je pomieszać, a maszyna pługa i zrobić coś złego. Jeśli użyliśmy assertExpectedEqualsActual
zamiast, może to uczynić nas mniej prawdopodobne, aby popełnić błąd, ale to nie daje więcej informacji do urządzenia (nie można zrozumieć po angielsku, a wybór nazwy nie powinna mieć wpływu na semantykę).
To, co sprawia, że podejście „ustrukturyzowane” jest bardziej preferowane, takie jak argumenty słów kluczowych, pola oznaczone etykietami, różne typy itp., Polega na tym, że dodatkowe informacje są również do odczytu maszynowego , dzięki czemu możemy wykryć nieprawidłowe użycie maszyny i pomóc nam zrobić wszystko dobrze. assertEqual
Sprawa nie jest tak źle, skoro jedynym problemem byłoby niedokładne wiadomości. Bardziej złowrogi może być przykład String replace(String old, String new, String content)
, który łatwo pomylić z String replace(String content, String old, String new)
którym ma zupełnie inne znaczenie. Prostym rozwiązaniem byłoby zabranie pary [old, new]
, która sprawiłaby, że błędy natychmiast spowodowałyby błąd (nawet bez typów).
Zauważ, że nawet w przypadku typów możemy nie powiedzieć „maszynie, co chcemy”. Na przykład anty-wzorzec zwany „programowaniem ciągłym” traktuje wszystkie dane jako ciągi znaków, co ułatwia pomieszanie argumentów (jak w tym przypadku), zapomnienie wykonania jakiegoś kroku (np. Ucieczkę), przypadkowe złamanie niezmienników (np. co nie do rozdzielenia JSON) itp.
Jest to również związane z „ślepotą logiczną”, w której obliczamy kilka wartości logicznych (lub liczb itp.) W jednej części kodu, ale przy próbie użycia ich w innej nie jest jasne, co tak naprawdę reprezentują, czy mamy pomieszane itp. Porównaj to np. z różnymi wyliczeniami, które mają opisowe nazwy (np. LOGGING_DISABLED
zamiast false
) i które powodują komunikat o błędzie, jeśli się pomieszamy.