Что такое выходной аргумент, как указано в Чистом коде Мартина?

14

На странице 45 «Чистого кода Роберта Мартина: Справочник по мастерству гибкого программного обеспечения» Мартин пишет, что выходных аргументов следует избегать. У меня возникают проблемы с пониманием значения «выходной аргумент» и почему их следует избегать.

Пример Мартина для выходного аргумента appendFooter(s);вызывает функцию public void appendFooter(StringBuffer report). Его улучшение кодаreport.appendFooter();

Возможно, это связано с отсутствием контекста кода, но я не вижу, как использование выходных аргументов считается плохим кодированием. Может ли кто-нибудь объяснить концепцию или дать дополнительный пример кода, чтобы понять это?

Будет ли следующая функция также рассматриваться как пример нечистого кода по вышеуказанному принципу?

int[] numberArray = {3, 5, 7, 1};
sortArray(numberArray);

Если вышеупомянутое является нарушением принципа Мартина не использовать выходные аргументы, было бы лучше иметь объект, который имеет массив в качестве поля, и функцию, которую можно вызывать для сортировки массива?

ObjectWithArrayField numberArray = new ObjectWithArrayField(3, 5, 7, 1);
numberArray.sort();
WP0987
источник

Ответы:

11

Боб Мартин просто говорит о читабельности .

Проблема с appendFooterпримером заключается в том, что если вы найдете строку кода appendFooter(s)где-то в программе, не сразу очевидно, что этот вызов принимает sв качестве входных данных и добавляет его куда-либо или sпросто передается для получения выходных данных этой функции. Чтобы быть уверенным, вам нужно проверить документацию функции. Однако такой вызов report.appendFooter()позволяет избежать этой проблемы: теперь гораздо более очевидно, что происходит.

Заметьте, однако, что Боб Мартин не говорит «никогда не используйте выходные аргументы», он говорит: «В общем, вам следует избегать этого, потому что это поможет вам сделать ваш код немного чище». Так что это не бредовое культовое правило, которому нужно следовать слепо.

Sortметоды для стандартных массивов и коллекций немного отличаются. Наличие sortметода, являющегося функцией-членом каждого стандартного типа данных массива, будет иметь несколько недостатков с точки зрения разработчика языка, например, наличие подобного метода Array.sortпозволяет сохранить его в стандартной библиотеке вне среды выполнения Java. Но если бы вы создали отдельный тип коллекции, который иногда нужно сортировать, добавление sortв качестве функции-члена действительно может быть лучшей идеей, чем помещение этого в отдельный класс.

Док Браун
источник
2
sortArray(numberArray), конечно, сортирует numberArrayна месте. Или он создает копию numberArray, сортирует копию и возвращает отсортированную копию без каких-либо изменений numberArray?
8bittree
@ 8bittree: это правда, но это не является предметом обсуждения - sort()метод контейнера также может работать на месте, без использования «выходного аргумента». Так что только потому, что sortArray(numberArray)метод на месте является абсолютно никакой причиной, которая оправдывает «форму выходного аргумента».
Док Браун
1
Моя точка зрения заключалась в том, что не совсем очевидно, что sortArray(numberArray)делает. Это может быть очевидно, если он не возвращает тот же тип, который принимает, то он должен быть на месте. Но не видя тип возвращаемого значения или если тип возвращаемого значения совпадает с типом ввода, неясно, не глядя на определение.
8bittree
1
@ 8bittree: Хорошо, вы меня поймали, я удалил ставку из кола из своего ответа. Однако проблема, которую вы описываете, не исчезает при использовании функции-члена - даже функция-член sort может вести себя так.
Док Браун
11

Это вопрос использования неожиданного механизма для возврата значения из функции, который обычно является результатом слишком большого выполнения функции или несогласованных обязанностей. Безусловно, лучший способ сообщить результат функции - использовать возвращаемое значение. Я надеюсь, что это само собой разумеющееся. В объектно-ориентированных языках вторым лучшим методом является изменение объекта.

Между этими двумя вариантами есть так много чистых, очевидных способов сообщить результат функции, что, если вы когда-нибудь захотите изменить аргументы как единственное средство, что-то пошло не так в вашей архитектуре. Вы должны перестроить свои классовые обязанности так, чтобы тот, кто занимается мутацией, владел данными в первую очередь.

Единственное исключение для очень общих алгоритмов. Например, алгоритм сортировки может по праву отделяться от сортируемых контейнеров, если он может быть применен в общем случае к любому типу контейнера с использованием его открытого интерфейса. Функция «один выстрел» appendFooterне имеет такого оправдания.

Карл Билефельдт
источник