о функциях (программистское)
Jan. 5th, 2010 03:37 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Эта запись будет интересна только программистам.
Предлагаю вопрос о том, как обустраивать код. Представьте себе, что у вас есть функция foo() (неважно, на каком языке; может, это метод, а не функция - неважно), вся работа которой - вызвать какие-то другие четыре функции A(), B(), C(), D(), которые расположены в других файлах и которые и делают всю основную работу. foo() должна приготовить для них аргументы, передать результаты работы A() в B(), и так далее. Логика foo() выглядит очень просто:
1. Вызвать A().
2. Вызвать B().
3. Вызвать C().
4. Если результат, который вернула C, интересный (условие на одну строчку), вызвать D().
Каждый из этих пунктов занимает где-то 5-10 строк: кроме самого вызова, из-за того, что он готовит правильные аргументы, проверяет, что функция вернула, плюс комментарий, плюс в нескольких местах пишет что-то в лог - в общем, всякие мелочи, но накапливается. Общий размер функции foo() - 40 строк.
Есыь предложение разбить функцию foo(), выделив каждый из логических кусков в отдельную функцию - скажем, doA(), doB() итд. - чтобы foo() только их вызывала. Противник этого преедложения говорит, что на данный момент нет никаких оснований считать, что кому-то еще понадобиться вызывать doA(), doB() итд., кроме foo(). Кроме того, тестировать отдельно doA(), doB() итд. тоже не надо - у главных функций A(), B() итд. есть свои тесты, и у foo() будет свой тест. С другой стороны, сторонник этого предложения, соглашаясь с этим, говорит, что все равно foo() слишком длинна, и что раз есть возможность выделить ее части в отдельные функции, правильным будет сделать этот рефакторинг. После него код будет читабельнее, понятнее, и удобнее для поддержки.
Как вы считаете? И какие аргументы выдвинули бы в поддержку своей точки зрения?
Предлагаю вопрос о том, как обустраивать код. Представьте себе, что у вас есть функция foo() (неважно, на каком языке; может, это метод, а не функция - неважно), вся работа которой - вызвать какие-то другие четыре функции A(), B(), C(), D(), которые расположены в других файлах и которые и делают всю основную работу. foo() должна приготовить для них аргументы, передать результаты работы A() в B(), и так далее. Логика foo() выглядит очень просто:
1. Вызвать A().
2. Вызвать B().
3. Вызвать C().
4. Если результат, который вернула C, интересный (условие на одну строчку), вызвать D().
Каждый из этих пунктов занимает где-то 5-10 строк: кроме самого вызова, из-за того, что он готовит правильные аргументы, проверяет, что функция вернула, плюс комментарий, плюс в нескольких местах пишет что-то в лог - в общем, всякие мелочи, но накапливается. Общий размер функции foo() - 40 строк.
Есыь предложение разбить функцию foo(), выделив каждый из логических кусков в отдельную функцию - скажем, doA(), doB() итд. - чтобы foo() только их вызывала. Противник этого преедложения говорит, что на данный момент нет никаких оснований считать, что кому-то еще понадобиться вызывать doA(), doB() итд., кроме foo(). Кроме того, тестировать отдельно doA(), doB() итд. тоже не надо - у главных функций A(), B() итд. есть свои тесты, и у foo() будет свой тест. С другой стороны, сторонник этого предложения, соглашаясь с этим, говорит, что все равно foo() слишком длинна, и что раз есть возможность выделить ее части в отдельные функции, правильным будет сделать этот рефакторинг. После него код будет читабельнее, понятнее, и удобнее для поддержки.
Как вы считаете? И какие аргументы выдвинули бы в поддержку своей точки зрения?
no subject
Date: 2010-01-05 01:44 pm (UTC)1. Наглядность.
2. Уменьшается сцепление ("coupling"). Кроме того, пространства имён функций doA(), doB()... явным образом сделаны независимыми.
3. Явно ограничивается время существования ненужных временных объектов, которые понадобилось создавать для выполнения doA(), doB()... Ну и стековый фрейм экономится заодно.
3.
no subject
Date: 2010-01-05 02:55 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2010-01-05 01:45 pm (UTC)no subject
Date: 2010-01-05 01:46 pm (UTC)Я сам в таких случаях не разбиваю, но не уверен, что это хорошо.
Мне кажется, что для повышения читабельности в таком случае можно воспользоваться комментариями и пробелами, искуственно выделяющими вызов А, В, С и Д.
no subject
Date: 2010-01-05 02:04 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2010-01-05 01:47 pm (UTC)no subject
Date: 2010-01-05 01:48 pm (UTC)1. doA-B-C могут где то еще пригодится
2. Обработка данных содержит значимый елемент логики, а не просто перекладывание яиц из листа в массив
То я бы сделал отдельные функции, в обратном случае можно, а иногда даже нужно оставить все вместе.
no subject
Date: 2010-01-05 01:48 pm (UTC)no subject
Date: 2010-01-05 02:07 pm (UTC)(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From: (Anonymous) - Date: 2010-01-05 05:04 pm (UTC) - Expand(no subject)
From:no subject
Date: 2010-01-05 01:48 pm (UTC)no subject
Date: 2010-01-05 01:54 pm (UTC)Логичнее делать сложный инструмент и учиться настраивать его чем разбираться с кучей мелких и простых
no subject
Date: 2010-01-05 10:30 pm (UTC)функция - она на то и функция, что-бы выполнять какую-то функцию ;)
единственный вариант когда надо выделять в отдельные функции - если в других местах A() также вызывается именно в виде doA() т.е. с теми же аргументами ну и т.п.
no subject
Date: 2010-01-05 01:59 pm (UTC)"жениться вам надо, барин". :)
no subject
Date: 2010-01-05 02:01 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2010-01-05 01:59 pm (UTC)С другой стороны, если эти 10 строк - это логи и ассерты и это повторяется в миллионе мест - можно подумать об AOP, так было бы изящнее.
no subject
Date: 2010-01-05 02:01 pm (UTC)no subject
Date: 2010-01-05 02:18 pm (UTC)так что +1
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2010-01-05 02:04 pm (UTC)40 строк - это много (лично меня пугают функции длиннее 20 строк :-). В этом месиве не будет четко видно, что вся суть функции - это последовательный вызов четырех остальных.
no subject
Date: 2010-01-05 02:05 pm (UTC)таков критерий.
а то что там длинный или короткий получается код -- это категорически неважно.
важна лишь адекватность реальному миру. Есть естественное деление которое можно отобразить функцией -- выделяем её. всё.
no subject
Date: 2010-01-05 02:12 pm (UTC)пожалуй, плюсану.
(no subject)
From:no subject
Date: 2010-01-05 02:05 pm (UTC)Но ради одного только размера функции я бы не стал добавлять лишний уровень.
no subject
Date: 2010-01-05 02:06 pm (UTC)Это достаточный аргумент. Совершенно не нужно чтобы doA() была интересна еще кому-то, кроме foo(), но если есть возможность вынести часть функционала в отдельную процедуру и дать ей имя - то почему бы и нет?
сравним:
(пример выдумал тут же)
(да, я знаю что $target и $userModel могут потребоваться еще раз тут же, в pushMessageAction())
(да, выйгрышь - только в читаемости кода)
(да, я считаю, что это - достаточно весомый аргумент в пользу рефакторинга)
no subject
Date: 2010-01-05 02:10 pm (UTC)(no subject)
From:(no subject)
From:)
Date: 2010-01-05 02:08 pm (UTC)Re: )
Date: 2010-01-05 02:56 pm (UTC)no subject
Date: 2010-01-05 02:09 pm (UTC)Если такой опасносте нет, то не все ли равно, как делать? Пусть люди самовыражаются, как хотят.
no subject
Date: 2010-01-06 07:40 am (UTC)no subject
Date: 2010-01-05 02:21 pm (UTC)no subject
Date: 2010-01-06 06:37 pm (UTC)no subject
Date: 2010-01-05 02:25 pm (UTC)no subject
Date: 2010-01-05 02:30 pm (UTC)1) код возврата скорее всего придется проверять в двух местах? И в doA() и потом, после вызова doA().
2) Что будет, когда добавится функция, которая вызывает те же функции, но по-другому? С другими аргументами? Будет добавлять doA2(), doB2()? Сделаем doAEx(), которая умеет вызывать A по-разному? Ради чего?
Не разбить
Date: 2010-01-05 02:26 pm (UTC)Читабельность 4-х и 40-ка строк неотличимы. Сам практикую "послойное" обустраивание. X() и foo() четко ложатся в два последовательных слоя, doX() - ни туда ни сюда. Если все же doX() нужны, до они должны маскировать X(), т.е. с уровня где лежит foo() должны быть видны только они.
no subject
Date: 2010-01-05 02:34 pm (UTC)1) удлиняется список функций ("оглавление"), действительно нужное искать будет сложнее
2) при повторном использовании, уверен, не будет сложности вынести всё наверх - а значит на текущий момент работа будет лишней
3) читабельность кода уменьшится: для того, чтобы "низкоуровнево" посмотреть, чем же конкретно занимаются эти свежесозданные функции, придётся пролистывать на другую функцию, затем возвращаться; читабельность можно улучшить, если добавить комментарии прямо в исходной функции, так, чтобы _визуально_ разбить код на несколько частей.
no subject
Date: 2010-01-05 02:45 pm (UTC)If doA(), doB(), ... represent good abstractions in themselves, they should be made so. If they are a cludge that cut across multiple abstractions, no way!
(There are very rare exceptions, like when where is a "nasty but necessary hack", it should live in a function of its own, with a fat comment, despite of the abstraction barrier.)
no subject
Date: 2010-01-05 02:47 pm (UTC)no subject
Date: 2010-01-05 02:53 pm (UTC)