Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы на экзаменационные вопросы по ЭСТП.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
399.63 Кб
Скачать

6. Аргументы функций. Приемлемое количество и качество аргументов. Побочные эффекты в функциях. Примеры

АРГУМЕНТЫ ФУНКЦИЙ

В идеальном случае количество аргументов функции равно нулю (нуль-арная функция). Далее следуют функции с одним аргументом (унарные) и с двумя аргументами (бинарные). Функций с тремя аргументами (тернарных) следует по возможности избегать. Необходимость функций с большим количеством аргу­ментов (полиарных) должна быть подкреплена очень вескими доводами — и все равно такие функции лучше не использовать.

Аргументы усложняют функции и лишают их значительной части концептуальной мощи.

Аргументы создают еще больше проблем с точки зрения тестирования. Только представьте, как трудно составить все тестовые сценарии, проверяющие правиль­ность работы кода со всеми комбинациями аргументов. Если аргументов нет — задача тривиальна. При одном аргументе все обходится без особых сложностей. С двумя аргументами ситуация усложняется. Если же аргументов больше двух, за­дача тестирования всех возможных комбинаций выглядит все более устрашающе.

Выходные аргументы запутывают ситуацию еще быстрее, чем входные. Читая код функции, мы обычно предполагаем, что функция получает информацию в аргументах, и выдает ее в возвращаемом значении. Как правило, никто не ожи­дает, что функция будет возвращать информацию в аргументах. Таким образом, выходные аргументы часто заставляют нас браться за чтение функции заново.

Если уж обойтись без аргументов никак не удается, постарайтесь хотя бы огра­ничиться одним входным аргументом.

СТАНДАРТНЫЕ УНАРНЫЕ ФОРМЫ

Существует два очень распространенных случая вызова функции с одним ар­гументом. Первая — проверка некоторого условия, связанного с аргументом, как в вызове boolean fileExists("MyFile"). Вторая — обработка аргумента, его преобразование и возвращение. Например, вызов InputStream fi 1е0реп( "MyFi le") преобразует имя файла в формате String в возвращаемое значение InputStream.

Не допускается испытание так называемых выходных аргументов. Большое количество аргументов затрудняет тестирование функций, так как необходимо проверять большое количество случаев.

Не следует использовать так называемые аргументы флаги. Их наличие явно указывает наличие у функций более одного действия. Передача логического значения функции — воис­тину ужасная привычка. Она немедленно усложняет сигнатуру метода, громко провозглашая, что функция выполняет более одной операции. При истинном значении флага выполняется одна операция, а при ложном — другая!

БИНАРНЫЕ ФУНКЦИИ

Функцию с двумя аргументами понять сложнее, чем унарную функцию. Напри­мер, вызов writeField(name) выглядит более доступно, чем writeField(outputStream.name). Хотя смысл обеих форм понятен, первая форма просто проскальзывает под нашим взглядом, моментально раскрывая свой смысл. Во второй форме приходится сделать непродолжительную паузу, пока вы не поймете, что первый параметр должен игнорироваться. И конечно, это в конечном итоге создает проблемы, потому что никакие части кода игнорироваться не должны. Именно в проигнорированных частях чаще всего скрываются ошибки.

ТЕРНАРНЫЕ ФУНКЦИИ

Разобраться в функции с тремя аргументами значительно сложнее, чем в бинар­ной функции. Проблемы соблюдения порядка аргументов, приостановки чтения и игнорирования увеличиваются более чем вдвое. Я рекомендую очень хорошо подумать, прежде чем создавать тернарную функцию.

Для примера возьмем стандартную перегруженную версию assertEquals с тремя аргументами: assertEquals (message, expected, actual). Сколько раз вы читали зна­чение message и думали, что перед вами expected?

ИЗБАВЬТЕСЬ ОТ ПОБОЧНЫХ ЭФФЕКТОВ

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

Для примера возьмем безвредную на первый взгляд функцию из листинга 3.6. Функция использует стандартный алгоритм для проверки пары «имя пользова­теля/пароль». Она возвращает true в случае совпадения или false при возник­новении проблем. Но у функции также имеется побочный эффект. Сможете ли вы обнаружить его?

Листинг 3.6. UserValidator.java

public class UserValidator {

private Cryptographer cryptographer;

public boolean checkPassword(String userName, String password)

{

User user = UserGateway.findByName(userName);

if (user != User.NULL) {

String codedPhrase = user.getPhraseEncodedByPasswordO;

String phrase = cryptographer.decrypt(codedPhrase. password);

if ("Valid Password".equals(phrase)) {

Session.initialize();

return true;

}

}

return false;

}

}

Разумеется, побочным эффектом является вызов Session.initialize(). Имя checkPassword сообщает, что функция проверяет пароль. Оно ничего не говорит о том, что функция инициализирует сеанс. Таким образом, тот, кто поверит имени функции, рискует потерять текущие сеансовые данные, когда он решит проверить данные пользователя.

Побочный эффект создает временную привязку. А именно, функция checkPassword может вызываться только в определенные моменты времени (когда инициали­зация сеанса может быть выполнена безопасно). Несвоевременный вызов может привести к непреднамеренной потере сеансовых данных. Временные привязки создают массу проблем, особенно когда они прячутся в побочных эффектах. Если без временной привязки не обойтись, этот факт должен быть четко оговорен в имени функции. В нашем примере функцию можно было бы переименовать в checkPasswordAndlnitial izeSession, хотя это безусловно нарушает правило «одной операции».