Я хотів би додати ще щось, на що натякають інші відповіді, але я не думаю, що це було згадано прямо:
@puck каже: "Все ще немає гарантії, що перший згаданий аргумент у назві функції справді є першим параметром".
@cbojar каже: "Використовуйте типи замість неоднозначних аргументів"
Проблема полягає в тому, що мови програмування не розуміють імен: вони просто трактуються як непрозорі, атомні символи. Отже, як і у коментарях до коду, не обов'язково існує кореляція між тим, що названа функцією, і як вона насправді функціонує.
Порівняйте assertExpectedEqualsActual(foo, bar)
з деякими альтернативами (з цієї сторінки та з інших країн), наприклад:
# 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)
Усі вони мають більшу структуру, ніж багатослівна назва, що надає мові щось непрозоре для погляду. Визначення та використання функції також залежить від цієї структури, тому вона не може не синхронізуватись із тим, що робить реалізація (наприклад, ім'я чи коментар).
Коли я стикаюся з такою проблемою або передбачаю її, перш ніж я засмучусь на своєму комп’ютері, я спочатку замикаюся, щоб запитати, чи взагалі «справедливо» звинувачувати машину. Іншими словами, чи було дано машині достатньо інформації, щоб відрізнити те, що я хочу, від того, що я просив?
Виклик, як-от, assertEqual(expected, actual)
має стільки ж сенсу, як assertEqual(actual, expected)
нам легко змішати їх, а машина може оратись вперед і робити неправильну справу. Якщо ми використовуємо assertExpectedEqualsActual
замість цього, це може змусити нас менше помилитися, але це не дає більше інформації машині (вона не може зрозуміти англійську мову, а вибір імені не повинен впливати на семантику).
Те, що робить «структуровані» підходи більш кращими, як аргументи ключових слів, мічені поля, різні типи тощо, - це те, що додаткова інформація також читається машиною , тому ми можемо встановити на машині неправильні звички та допомогти нам робити все правильно. Справа assertEqual
не надто погана, оскільки єдиною проблемою будуть неточні повідомлення. Більш зловісний приклад може бути String replace(String old, String new, String content)
, який легко сплутати з тим, String replace(String content, String old, String new)
що має зовсім інше значення. Простим засобом було б взяти пару [old, new]
, яка б помилками викликала помилку негайно (навіть без типів).
Зауважте, що навіть за типи, ми можемо виявити, що ми не говоримо машині, що ми хочемо. Наприклад, антидіаграма під назвою "строго типізоване програмування" трактує всі дані як рядки, що дозволяє легко змішувати аргументи (як у цьому випадку), забути виконати якийсь крок (наприклад, уникнути), щоб випадково зламати інваріантів (наприклад, зробити нерозбірливий JSON) тощо.
Це також пов'язано з "булевою сліпотою", де ми обчислюємо купу булевих (або цифр тощо) в одній частині коду, але при спробі їх використання в іншій не зрозуміло, що вони насправді представляють, чи ми їх переплутали і т. д. Порівняйте це, наприклад, з різними переліками, які мають описові назви (наприклад, LOGGING_DISABLED
а не false
) і які викликають повідомлення про помилку, якщо ми їх змішуємо.