Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Как правильно писать функции?


Сейчас читаю книгу "Чистый код" и вот решил уточнить у разработчиков с большим опытом, чем у меня.

Тут написано, что следует стремиться к написанию унарных функций (которые содержа
один аргумент), а лучше чтоб и вовсе ничего не принимала, чтоб было просто красивое имя функции, прочитав которое можно интуитивно понять, что она делает.

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


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


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

Или не так я рассуждаю?
    


Ответы

Ответ 1



Я соглашусь с автором книги - чем меньше аргументов тем лучше. Во-первых легче читат код, нету путаницы какая переменная что означает, во-вторых легче писать тесты так ка количество тестов чтобы покрыть все возможные комбинации входных данных зависит от количеств последних. Я думаю это очевидно. Еще один довод в пользу сокращения количества аргументов это передназначение функции. Если у вас много входных данных, это значит, что скорее всего ваша функция делает слишком много и ее нужно разделить на несколько более простых функций. Здесь я также соглашусь с @cpp_user что красивый код может быть не настолько быстрым на сколько красивым. В книге сказано что в идеальном случае функция должна быть нулярная(не иметь аргументов но это больше теория. Автор признает что вы всегда будете вынуждены писать унарные или бинарные функции. Вам почти всегда нужно что передать в функцию чтобы получить результат который является 'функцией' исходных данных. Так что функции с входными параметрами это не плохо, но следует аккуратнее подходить к проектированию кода и минимизировать количество аргументов. Не помню если это отражено в книге или нет но избегайте случаев когда все (несколько) аргументов функции имеют один и тот же тип. Это часто приводит к ошибкам. И еще, глобальные переменные - это зло, об этом написано миллион книжек.

Ответ 2



Факт унарности(а точнее даже сказать, атомарности) функций и методов - один из тех к которым почти невозможно прийти в 100% случаев на практике, но к которому, действительно, всегда нужно стремиться. Суть этого совета не в том, чтобы все параметры тупо глобализовать, а в том, чтоб прийти к хорошему архитектурному решению. Потому что если функция/метод/процедура/whatever принимает на вход более 3-5 параметров, то это в большинстве случаев бьет по качеству архитектуры и указывает на явные бреши в ней. Инкапсуляция. Какова главная цель инкапсуляции? - сформировать хорошую абстракцию, для взаимодействи с которой нужно знать о деталях ее реализации настолько мало, насколько это возможно(в совершенстве - ничего). Сравните следующие два набора: MyDateClass{ private Date date; private Time time; /* 1 */ public MyDateClass(Date date, Time time){...} public Date GetDate(){ return date; } public Time GetTime(){ return time; } ... /* 2 */ public MyDateClass(uint timestamp){...} public String GetPrintable(){ return date+" : "+time; } public void Add(MyDateClass mdClass){ ... } ... } Какой из наборов, по вашему мнению, раскрывает детали реализации? Правильный ответ: первый, потому что он убивает смысл инкапсуляции как таковой. И причина этому - избыточность, которая появляется в результате бОльшего числа аргументов конструктора 1. Вывод: чем меньше параметров принимает на вход субъект, тем лучше инкапсуляция параметры раскрывают детали реализации. Для более объективного понимания принципов проектирования функций/методов/... я бы советовал вам почитать 7 главу Совершенного Кода "Высококачественные методы".

Ответ 3



Таковое пожелание напрямую следует из принципа единственной обязанности. Метод, п хорошему, должен выполнять одну единственную, максимально атомарную, задачу. И я вас уверяю, найдется очень мало таких задач, для выполнения которых понадобится более двух параметров. Там, где их понадобится больше - имеет смысл подумать о создании объекта, обладающего всему необходимыми свойствами/данными и передавать его. Дурацкий, надуманный пример, зато отлично раскрывающий суть. Допустим нам надо увеличивать значение некой переменной на единицу. Вариант первый. Передаем в функцию два параметра и результат присваиваем переменной i=i+1 Вариант второй. Передаем в функцию один аргумент, с помощью которого изменяем переменную. i+=1 Вариант третий, идеальный для данной атомарной задачи. Используем функцию вообщ без аргументов. i++

Ответ 4



От себя могу добавить: Пишите функции так, чтобы было максимально легко понять что она делает - хороше название, первое на что нужно обратить внимание. Каждая функция должна выполнять конкретную задачу только на одном уровне абстракции - не мешайте внутри низкоуровневые вычисления и высокоуровневый код. Если в нее нужно передать много параметров, проще сделать класс-аргумент - получите унарную функцию. Есть нехорошая индусская практика, что функция должна содержать один вход - оди выход. При этом читать такие функции, в особенности если они более 1 страницы просто ужасно - используйте принудительные выходы (return). В целом старайтесь сделать функцию настолько простой, насколько это возможно. Нагородит тонну кода очень легко, так же как и запутаться в нем, а вот решить задачу просто намного сложнее. Так же учитывайте задачи которые перед вами стоят. Если требуется максимальная скорость то вызов кучи функций внутри других функций, может негативно сказаться на производительности приложения, в особенности на рекурсивных функциях или функциях с длинными цепочками вызовов.

Комментариев нет:

Отправить комментарий