Это мини-серия статей по написанию поддерживаемого объектно-ориентированного кода без лишней нервотрепки.
Предыдущие части: Часть 1, Часть 2.
Аргументы, аргументы, аргументы
- Длинные списки аргументов в функции крайне трудны для вызываемого кода. К тому же, тогда можно передать аргументы в неправильном порядке с минимальной безопасностью типов и снизить читабельность кода.
- Их трудно прочесть и понять. Приходится дважды проверять сигнатуру функции при каждом вызове.
“Каждый из нас может запутаться, сбиться с толку и изменить свое состояние потока в процессе чтения кода. Поэтому и приходится проверять все дважды» — Дядя Боб Мартин.
- Аргументы осложняют тестирование функции. Крайне трудно писать тестовые примеры для корректной работы всех возможных сочетаний аргументов.
Поэтому вместо того, чтобы бездумно пользоваться аргументами, видя в них одно лишь удобство, необходимо проявлять осмотрительность и ответственно подходить к использованию каждого.
Структура
Чем лучше функция, тем меньше в ней аргументов. В идеале — их нет.
ниладическая (нулевая)> монадическая> диадическая> триадическая> полиадическая
Тот же принцип распространяется на конструкторы.
Не забывайте о рефакторинге
- Объекты аргумента. Если в функциях вы передаете три и более аргументов, то сядьте и подумайте. Если три и более переменные так тесно связаны, что даже передаются вместе в функцию, то почему бы не превратить их в объект? Перегрузка. Перегрузка функции делается для того, чтобы, например, клиент мог вызвать нужную версию функции, указав необходимые параметры.
- Шаблон «Строитель»: Иногда перегрузка функции приводит к неожиданному эффекту, более известному как анти-паттерн «Телескопический конструктор». Во избежание этого, Джошуа Блок во втором издании «Java. Эффективное программирование» предлагает использовать шаблон «Строитель» для работы с конструкторами, требующими большого количества параметров.
- Изменяемое состояние: *Не рекомендуется*
Пожалуй, самым известным и презираемым подходом в разработке ПО с использованием состояния для сокращения параметров в методах является создание глобальных переменных экземпляров. Это решение далеко не идеально, но в ряде случаев работает на ура. Пользуйтесь им с осторожностью, особенно в приложениях с высокой многопоточностью.
«Все глобальные данные виновны, пока не доказано обратное». — Мартин Фоулер
Смерть от логических значений
Чаще всего при передачеboolean
в функцию вы объявляете, что написали функцию, которая выполняет два действия: одно для true
и одно для false
. Лучше напишите две функции — по одной на каждый сценарий.
Логическое значение, висящее в списке аргументов, может стать главным источником бед и ошибок. Каким будет истинное значение? А чего можно ждать от ложного? Если это не очевидно из названия функции и аргумента, то каждый раз придется копаться в деталях реализации функции и проверять, что именно передается в true
. С передачей двух логических значений дела обстоят еще хуже. Функция, принимающая два логических значения, выполняет целых четыре действия!
Попробуйте отгадать логические значения, их порядок и поведение:
Вынос мозга, не так ли? Избегайте неопределенности и лишних проверок. Переходите на шаблон «Строитель»:
Нулевая защита Null
Передавать null
в функцию или писать функцию, ожидающую значения null
, — также плохо, как передавать логические значения. По сути, даже хуже. Ведь вы даже не уверены в том, что в ней прописано всего лишь два возможных состояния.
Разумеется, есть некое поведение для всех не пустых значений и одно — для сценария с null
. В данном случае, лучше сразу создавать две функции, одна из которых будет принимать не пустой аргумент, а другая — не сможет принять его вовсе. Не пользуйтесь null
как псевдологическим значением.
Давайте начистоту: безопасное программирование — отстой. Это же сущий кошмар засорять код проверками на null
и ошибки. Мы не хотим думать о том, что наши коллеги могли позволитьnull
незаметно прокрасться в наши функции. Мы также не хотим доверять модульным тестам, не разрешающим передаватьnull
.
Такая схема не работает с публичными API, поскольку мы не знаем, кто и что собирается передавать. Если ваш язык поддерживает аннотацию @NotNull
или схожую концепцию, то воспользуйтесь им для публичных функций.
Перевод статьи Arun Sasidharan: Object Oriented Tricks: #3 Death By Arguments