Я хотел бы добавить кое-что еще, на что намекают другие ответы, но я не думаю, что было упомянуто явно:
@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
) и которые вызывают сообщение об ошибке, если мы их перепутали.