![]() |
![]() |
|
||
![]() |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [15 февраля 2002 г.] |
Эта статья фактически представляет собой продолжение темы одиннадцатой наблы. Я попробую высказать некоторые предложения насчет того, как желательно оформлять код собственной программы. Преимущественно речь пойдет о Perl, и вот почему.
Язык Perl имеет дурную славу. Особенно когда речь заходит о том, какой же некрасивый код программист вынужден на нем писать. Конечно, в этом есть большая доля истины (пожалуй, даже больше половины), однако многое зависит также и от того, насколько аккуратен человек, пишущий на Perl. В мире существуют тысячи больших и маленьких программ, которые были написаны в спешке или просто неаккуратно. Именно из-за них Perl называют языком, на котором можно писать, но не читать его. Но попробуем сломать стереотипы, хотя бы некоторые.
Конечно, любой программист знает: переменные должны называться так, чтобы потом можно было понять, что они обозначают. Однако здесь, как и во всем, нужно знать меру.
Вот несколько примеров. Конечно, они не бесспорны, и многие люди не согласятся с выводами, которые я тут делаю. Что же, критика всегда приветствуется. Помните также, что каждый программист придерживается своего собственного стиля оформления исходников, но в то же время все будут просто счастливы, если бы весь мир вдруг однажды утром проснулся и начал писать в едином стиле.
Первый
use CGI::Carp qw(fatalsToBrowser);
Мы видим, что строка чересчур уж длинна. Даже для того действия, которое она выполняет (я имею в виду, что, хотя ошибки и отображаются в браузере,
Второй
Тут, правда, экономия всего в одну букву, что не очень критично.
|
Во-вторых, в английском языке не так уж и много слов, начинающися с
Я рекомендую применять эти правила и при программировании на Perl (особенно на нем). К сожалению, им далеко не всегда следуют авторы стандартных модулей.
Не то, что в Java, где с этим очень |
Третий (а также четвертый, пятый и т. д.)
Итак, все, к чему я
Как ни странно, но остроумные сокращения запоминаются даже лучше, чем исходные слова. Этим в последнее время постоянно |
А сейчас я выскажу еще одну, гораздо более еретическую мысль. Уведите детей от монитора. Готовы?.. Итак. Известно, что в объектно-ориентированной программе примерно половина всех имен функций начинаются со слов
Еще лучше в этом вопросе поступили разработчики Delphi и C# (в которых можно объявлять свойства класса и работать с ними, как с обычными полями структуры). К сожалению, корпорация Sun никогда не задумывалась, что будущее за свойствами (и в этом вопросе C# явно обскакал Java), а потому наизобретала всяких спецификаций о том, как надо писать get- и set-методы (например, JavaBeans).
|
Теперь о переменных. Мы уже договорились, что нужно начинать их с маленькой буквы (в
Теперь о переменных, используемых как счетчики различных циклов. Некоторые считают, что они ничем не отличаются от всех остальных объектов в программе, а значит, вполне возможно написать, например, такой код:
# int factorial(int $n)
# Возвращает факториал аргумента.
sub factorial
{ my ($number)=@_;
my $factorial=1;
for(my $index=1; $index<=$number; $index++) {
$factorial*=$index;
}
return $factorial;
}
Да-да, я |
Хотя он и хорош для целей демонстрации возможностей языка, задача, решаемая им, настолько проста, что имена переменных выглядят явно излишне длинными. Сравните со следующим кодом:
# int factorial(int $n)
# Возвращает факториал аргумента.
sub factorial
{ my ($n)=@_;
my $r=1;
for(my $i=1; $i<=$n; $i++) {
$r*=$i;
}
return $r;
}
Данный пример является лишь моделью, поэтому он и не так нагляден, как хотелось бы. Реальное преимущество от коротких имен переменных можно почувствовать только в больших программах. Что же это за преимущество?.. Очень просто: короткие имена (конечно, в разумных пределах) не загромождают код и позволяют лучше видеть его логику. |
Естественно, сокращать до «абсурдных» размеров следует не все имена, а только попадающие под следующие категории.
Все эти имена удобно использовать лишь порознь, но не все вместе. Вообразите себе мучения программиста, пытающегося прочитать небольшой участок кода, в котором используется десяток однобуквенных переменных. В таком случае, конечно, лучше использовать более длинные идентификаторы.
Многие люди придерживаются идеи о том, что хорошо написанная программа должна быть самодокументированной (не содержать комментариев, но, несмотря на это, легко читаться). К сожалению, в больших проектах этого добиться обычно не удается. Комментарии жизненно необходимы на всех этапах программирования, как для вас, так и для остальных людей.
Стереотип о самодокументировании имеет много общего со стереотипом о длинных именах идентификаторов, потому что именно таким способом обычно пытаются добиться ясности кода. К сожалению, это удается далеко не всегда.
Если вы не писатель в душе, то ненавидите вставлять комментарии во время кодирования. В крайнем случае, откладываете это «на потом»: «Я вставлю комментарии после того, как программа заработает». Обычно это «потом» так и не настает, так что программа остается без комментариев, а значит, низкого качества (что бы там ни говорили, в большинстве случаев это так).
Про то, как желательно составлять комментарии, мы поговорим чуть ниже, а пока замечу одну деталь. Вы можете комментировать вашу программу, как вам вздумается. Но гораздо удобнее и приятнее будет придерживаться одного определенного стиля комментирования, как вы делаете это при написании кода. Четко выраженный стиль комментирования позволит также в перспективе создать программу, которая будет «шерстить» ваш код, выделяя из него комментарии и идентификаторы. Хороший пример
Разработчики Perl, видимо, думали над этим вопросом, потому что встроили в язык инструкции специального
=head2 Математические функции.
=over 5
=item int factorial(int $n)
Возвращает факториал своего аргумента.
=cut
sub factorial
{ my ($n)=@_;
my $r=1;
for(my $i=1; $i<=$n; $i++) {
$r*=$i;
}
return $r;
}
К сожалению, тут имеет место совершенно явная недоработка: для того, чтобы инструкции POD воспринимались инструментами формирования документации, необходимо, чтобы каждая директива была окружена с обеих сторон пустыми строками! Это весьма прискорбно, учитывая, что ни один программист в здравом уме не будет так «разбавлять» свою программу (как это показано в примере). Мне совершенно непонятно, почему разработчики спецификации POD, находясь, вообще говоря, в авангарде технологии (насколько мне известно, в тот момент не существовало другого языка, кроме Perl, позволяющего проделывать такие вещи), допустили столь чудовищную ошибку. Кто-то что-то говорил о лучшей производительности программ выделения
Тем не менее, POD все еще применяется (и весьма успешно) для создания технической документации, но обычно одним большим блоком и после конца скрипта (чаще всего за инструкцией
Это утверждение прямо противоположно предыдущему, но никто и не говорил, что мы будем рассматривать только стереотипы из «одной корзины». Если вас иногда «заносит» и вы начинаете писать огромное количество комментариев в своем коде (а именно это частенько происходит со мной), если в вашей программе комментарии составляют более половины текста, время остановиться и задуматься. Налицо некоторая ошибка, но в чем же она заключается?..
И тут мы сталкиваемся с проблемой, у которой, вообще говоря, нет определенного решения. Можно лишь дать некоторые рекомендации и примеры того, когда следует писать комментарии, а
Нет, не садиться комментировать на три страницы, ни в коем случае! Прежде всего, стоит подумать, нельзя ли участок программы написать получше. Возможно, это потребует перелопачивания некоторого объема кода выше и ниже только что вставленного фрагмента. В принципе, не стоит этого бояться: обычно любая реорганизация программы ведет к ее улучшению, а никак не к ухудшению (в крайнем случае, всегда можно вернуться к предыдущей архивной копии). К тому же, возможно, улучшение будет столь значительным, что в будущем вы скажете себе за него спасибо.
Предположим, вы все-таки решили вставить комментарий (естественно, это когда-нибудь произойдет). Что именно в нем написать?.. Не пытайтесь описать кусок кода изолированно: лучше опишите общими словами, что он делает, а также какие результаты будут получены после его выполнения. Вот пример неверного комментария:
# $elt хранит первый элемент списка.
my $elt=$obj->getFirst();
# Инициализируем нулем.
my $n=0;
# Пока текущий элемент существует...
while($elt) {
# Увеличить $n...
$n++;
# $elt = следующий элемент
$elt=$elt->getNext();
}
Фактически, он может показаться верным с первого взгляда, но, тем не менее, тут допущена грубая ошибка, хуже
Приведу теперь пример того, как можно откомментировать этот код. Наверняка мой вариант тоже не идеален, но он, по крайней мере, более информативен.
# $obj - список окон на рабочем столе.
# Считаем, сколько их.
my $elt=$obj->getFirst();
my $n=0;
while($elt) {
$n++;
$elt=$elt->getNext();
}
# Теперь $n - число окон.
Наверное, вы поняли, что я сделал: просто убрал комментарии из мест, где и так все понятно. Перебирать связанный список можно только одним
Так как в этом примере читабельность кода между комментариями, в общем-то, не имеет особого значения, можно записать его короче и # $obj - список окон на рабочем столе.
# Считаем, сколько их.
my $n=0;
for(my $e=$obj->first(); $e; $e=$e->next(), $n++) {}
# Теперь $n - число окон.
К моему большому сожалению, длинная строчка этого кода не влезала бы на страницу в браузере, если бы я не сократил имена методов до |
Итак, комментарии к коду должны быть такими, чтобы, если бы кто-нибудь вдруг стер весь код, оставив только эти самые комментарии, по ним нельзя было бы восстановить программу в первозданном виде. Это утверждение аналогично предыдущему: комментарии не должны лишь повторять то, что делает
Задержим взгляд на последней мысли. Что такое вообще «тонкое место»? Как его можно распознать?.. Вообще говоря,
# $obj - список окон на рабочем столе.
# Считаем, сколько их.
my $elt=$obj->getFirst();
my $n=0;
# ВНИМАНИЕ! Следующее далее условие не работает,
# если список является кольцевым, а не
# оканчивается элементом undef.
while($elt) {
$n++;
$elt=$elt->getNext();
}
# Теперь $n - число окон.
Внося дополнительный комментарий в этот «распухающий» демонстрационный код, мы тем самым оказываем помощь себе же, когда в будущем захотим переделать структуру списка в
Конечно, из кода и так ясно, что список не может быть |
Грамотный и добросовестный программист всегда ведет журнал изменений своего проекта, что тоже можно отнести к комментированию. Я его обычно не
Если в некотором месте кода вы нашли ошибку и хотите ее исправить, постарайтесь оставить на ее месте не «дыру» (то есть, пустое место), а комментарий, описывающий, в чем же была проблема. Особенно это относится к вашим же решениям улучшить тот или иной алгоритм в конкретной ситуации. Игнорирование этой рекомендации может привести к весьма курьезным случаям, когда вы 3 раза подряд наступаете на одни и те же грабли, «улучшая» код, а потом вдруг вспоминая, что именно это улучшение вы уже неоднократно делали и выяснялось: оно не может работать.
Собственный опыт подсказывает: если вы изо всей силы избегаете писать код, символы которого «залезают» за правый край поля в редакторе (т.е. пересекают 80-й столбец), это может помочь лишь в одном
Может быть, тут как-нибудь можно еще задействовать и бабушкину печатную машинку, а то что она без дела на шкафу пылится...
|
Сначала кажется, что, если код не помещается на страницу по горизонтали, его будет трудно читать. Можете проверить сами: это не соответствует действительности. Если даже вы и заинтересуетесь одной из немногих строк, которые «не влезли», то наверно уж не побрезгаете пару раз нажать стрелку вправо или немного поелозить линейкой прокрутки (для любителей меткой стрельбы). Конечно, я не призываю писать код квадратиком, а значит, 95% строк программы будут все-таки не длиннее 50-60 символов. И уж как писать эти
Длинные строки довольно удобны в следующих случаях.
@r=sort { -d $a && !-d $b && -1 or -d $b && !-d $a && 1 or $a cmp $b } readdir(DIR);
Я вынужден уже в третий раз оправдываться: окно браузера несовершенно, а потому использовать его для выражения программистских |
use lib ($ENV{HOME}||"$ENV{DOCUMENT_ROOT}/.."). '/библиотеки';
См. пятую наблу, если интересуетесь, зачем кому-то может понадобиться такой код.
Если я вас так и не убедил, последний аргумент. Почему вы считаете, что вам мешает «вылезание» текста за правый край, в то время как на выход за нижний край страницы вы не обращаете ровно никакого внимания, называя это «скроллингом»?.. Если вы скажете, что «у вас мышь с колесиком», то я отвечу: часто в этом случае колесика 2, для вертикальной и горизонтальной прокрутки. Кроме того, стрелки на клавиатуре пока что никто не отменял. |
Наверное, каждый программист слышал о том, что вызов функции в программе требует некоторых накладных расходов, связанных с передачей параметров, сохранением точки возврата и т. д. Многие утилиты (такие как
Тем не менее, существует несколько хороших рекомендаций от самих мэтров программирования (Кнут, Страуструп), которые заявляют: все только что
Первая и, пожалуй, самая неожиданная мысль: длина функций в программе не должна превышать 20 строк. Если функция длинее, попытайтесь разбить ее на как можно более независимые блоки (подфункции), полезные и сами по себе. Девять шансов из десяти, что они потом пригодятся вам и в других местах программы.
Конечно, цифру 20 не нужно понимать как «божественное число». Это лишь примерная оценка, верная для подавляющего большинства функций программы. Тут может быть и 30, и 40 строк, но важно понимать, что строчек должно быть немного гораздо меньше, чем все мы привыкли писать.
|
Таким образом, если вы пишете функцию и обнаруживаете, что она длинее 20 строк (кстати, то же относится и к главной исполняемой части программы), значит, что-то тут не так. Возможно, стоит подумать: нельзя ли кое-что исправить в концепции, чтобы появилась возможность не писать длинный код. Чаще всего ответ на этот вопрос положителен.
Но откуда вообще берется совет об ограничении на длину функции?.. В расчет принимается лишь одно: программы с мелкими функциями значительно легче отлаживать. Например, вы можете вставить на место еще не отлаженной функции «заглушку», которая будет возвращать одно и то же тестовое значение, и, таким образом, проверить правильность работы окружающего кода. Но все это, конечно, выходит за рамки данной наблы.
Второе замечание (о цене вызова функции) для таких языков, как Perl, вообще часто оказывается абсурдным. По крайней мере, пока программа не начнет явно «тормозить», и пока вы не запустили профайлер и не определили, где же ее «узкое место», глупо пытаться что-то оптимизировать. Это напоминает экономию на спичках.
О том, как запустить |
И последнее: большое число параметров
$s = IO::Socket::INET->new( PeerAddr => 'www.perl.org', PeerPort => 'http(80)', Proto => 'tcp', #! );
Обратите внимание на строку, помеченную как |
Но не впадайте в искушение использовать «хэш аргументов» абсолютно во всех функциях. У авторов модуля были реальные причины вводить подобный синтаксис: во-первых, в будущем вполне могли добавляться новые параметры конструктора, а во-вторых, даже существующих параметров сейчас весьма много, и большинство из
Итак, прием с «хэшем аргументов» стоит применять в случае, если параметров у функции много и большинство из них необязательны (то есть, могут иметь значения по умолчанию). Во всех остальных ситуациях избегайте этого трюка, потому что он слишком громоздок для частого применения.
На этом позвольте объявить об окончании сегодняшней наблы. Я выкладываю ее на сайт еще «сырой», подразумевая в ближайшие дни некоторую «чистку и шлифовку». Продолжение следует.
|
![]() |
| ||||||||||||||||||||||||||||
| Дмитрий Котеров | 15 февраля 2002 г. ©1999-2010 | | Контакт | Вернуться к оглавлению |