Сохраняйте небольшое количество аргументов функции
Причина
Большое количество аргументов создаёт возможности для путаницы. Передача большого количества аргументов часто обходится дороже, чем альтернативы.
Обсуждение
Две наиболее распространённые причины, по которым функции имеют слишком много параметров:
Отсутствует абстракция, поэтому составное значение передаётся как отдельные элементы, а не как единый объект, обеспечивающий инвариант. Это не только расширяет список параметров, но и ведёт к ошибкам, поскольку составляющие значения больше не защищены принудительным инвариантом.
- Отсутствует абстракция.
Функция пытается делать больше одной работы и, вероятно, должна быть реструктурирована.
- Нарушение принципа «одна функция — одна ответственность».
Пример
merge() стандартной библиотеки находится на пределе того, что мы можем удобно обрабатывать:
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
Обратите внимание, что это из-за проблемы 1, описанной выше, — отсутствующей абстракции. Вместо передачи диапазона (абстракции) STL передаёт пары итераторов (неинкапсулированные составляющие значения).
Здесь у нас четыре параметра шаблона и шесть параметров функции. Для упрощения наиболее частых и простых вариантов использования аргумент сравнения можно задать по умолчанию как <:
template<class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
Это не уменьшает общую сложность, но уменьшает поверхностную сложность, представляемую многим пользователям. Чтобы действительно уменьшить количество аргументов, нужно объединить аргументы в абстракции более высокого уровня:
template<class InputRange1, class InputRange2, class OutputIterator>
OutputIterator merge(InputRange1 r1, InputRange2 r2, OutputIterator result);
Группировка аргументов в «пачки» — это общая техника для уменьшения количества аргументов и увеличения возможностей для проверки.
В качестве альтернативы мы могли бы использовать концепт стандартной библиотеки для определения понятия трёх типов, которые должны быть пригодны для слияния:
template<class In1, class In2, class Out>
requires mergeable<In1, In2, Out>
Out merge(In1 r1, In2 r2, Out result);
Пример
Профили безопасности рекомендуют заменять
void f(int* some_ints, int some_ints_length); // ПЛОХО: стиль C, небезопасно
на
void f(gsl::span<int> some_ints); // ХОРОШО: безопасно, с проверкой границ
Здесь использование абстракции даёт преимущества в безопасности и надёжности и естественным образом также уменьшает количество параметров.
Примечание
Сколько параметров — это слишком много? Старайтесь использовать менее четырёх (4) параметров. Есть функции, которые лучше всего выражаются с четырьмя отдельными параметрами, но их не много.
Альтернатива: Используйте лучшую абстракцию: группируйте аргументы в значимые объекты и передавайте объекты (по значению или по ссылке).
Альтернатива: Используйте аргументы по умолчанию или перегрузки, чтобы наиболее распространённые формы вызовов могли выполняться с меньшим количеством аргументов.
Контроль
- Предупреждать, когда функция объявляет два итератора (включая указатели) одного типа вместо диапазона или представления.
- (Не применяется) Это философский руководящий принцип, нецелесообразный для прямой проверки.