Главная страница

Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по


Скачать 5.88 Mb.
НазваниеРуководство по стилю программирования и конструированию по
АнкорСовершенный код
Дата31.03.2023
Размер5.88 Mb.
Формат файлаpdf
Имя файлаСовершенный код. Мастер-класс. Стив Макконнелл.pdf
ТипРуководство
#1028502
страница32 из 106
1   ...   28   29   30   31   32   33   34   35   ...   106
ГЛАВА 10 Общие принципы использования переменных
235
Пример хорошей инициализации переменных (Visual Basic)
Dim accountIndex As Integer accountIndex = 0
’ использование переменной accountIndex
Dim total As Double
Переменная total объявляется и инициализируется непосредственно перед ее использованием.
total = 0.0
’ использование переменной total
Dim done As Boolean
Переменная done также объявляется и инициализируется непосредственно перед ее использова- нием.
done = False
’ использование переменной done
While Not done
Второй вариант лучше первого по нескольким причинам. Пока выполнение пер#
вого примера дойдет до фрагмента, в котором используется переменная
done, она может оказаться измененной. Даже если при написании программы это не так, нельзя гарантировать, что этого не произойдет после нескольких ее изменений. Кроме того,
в первом примере все переменные инициализируются в одном месте, из#за чего создается впечатление, что все они используются на протяжении всего метода, тогда как на самом деле переменная
done вызывается только в его конце. Наконец, в ре#
зультате изменений программы (которые неизбежно придется вносить, и не толь#
ко при отладке) код, использующий переменную
done, может оказаться заключен#
ным в цикл, при этом переменную каждый раз нужно будет инициализировать за#
ново. Во второй пример в этом случае придется внести лишь небольшое измене#
ние. Первый пример слабее защищен от досадных ошибок инициализации.
Два этих фрагмента иллюстрируют Принцип Близости: груп#
пируйте связанные действия вместе. Этот принцип предпо#
лагает также близость комментариев к описываемому ими коду, близость кода настройки цикла к самому циклу, груп#
пировку команд в линейных участках программы и т. д.
В идеальном случае сразу объявляйте и определяйте каждую переменную
непосредственно перед первым обращением к ней При объявлении пере#
менной вы указываете ее тип. При определении вы присваиваете ей конкретное значение. Если язык позволяет (к таким языкам относятся, например, C++ и Java),
переменную следует объявлять и определять перед фрагментом, в котором она используется впервые. В идеале каждую переменную следует определять при ее объявлении:
Перекрестная ссылка О группи- ровке связанных действий см.
раздел 10.4.
>
>

236
ЧАСТЬ III Переменные
Пример хорошей инициализации переменных (Java)
int accountIndex = 0;
// использование переменной accountIndex
Переменная total инициализируется непосредственно перед ее использованием.
double total = 0.0;
// использование переменной total
Переменная done также инициализируется непосредственно перед ее использованием.
boolean done = false;
// использование переменной done while ( ! done ) {
Объявляйте переменные по мере возможности как final
или const Объявив переменную как final в Java или const
в C++, вы можете предотвратить изменение ее значения пос#
ле инициализации. Ключевые слова
final и const полезны для определения констант класса, исключительно входных параметров и любых ло#
кальных переменных, значения которых должны оставаться неизменными после инициализации.
Уделяйте особое внимание счетчикам и аккумуляторам Переменные i, j,
k, sum и total часто играют роль счетчиков или аккумуляторов. Нередко програм#
мисты забывают обнулить счетчик или аккумулятор перед его использованием в очередной раз.
Инициализируйте данные'члены класса в его конструкторе Подобно пе#
ременным метода, которые следует инициализировать при вызове каждого мето#
да, данные класса следует инициализировать в его конструкторе. Если в конструк#
торе выделяется память, в деструкторе ее следует освободить.
Проверяйте необходимость повторной инициализации Спросите себя, нуж#
но ли будет когда#нибудь инициализировать переменную повторно: например, для применения в цикле или для переустановки ее значения между вызовами метода.
Если да, убедитесь, что команда инициализации входит в повторяющийся фраг#
мент кода.
Инициализируйте именованные константы один раз; переменные иници'
ализируйте в исполняемом коде Если переменные служат для имитации име#
нованных констант, вполне допустимо инициализировать их один раз при запуске программы. Инициализируйте их в методе
Startup(). Истинные переменные ини#
циализируйте в исполняемом коде неподалеку от места их вызова. Очень часто метод, который первоначально применялся один раз, после изменения програм#
мы вызывается многократно. Переменные, которые инициализируются в методе
Startup() уровня программы, не будут инициализироваться повторно.
Перекрестная ссылка О группи- ровке связанных действий см.
также раздел 14.2.
>
>

ГЛАВА 10 Общие принципы использования переменных
237
Настройте компилятор так, чтобы он автоматически инициализиро'
вал все переменные Если ваш компилятор поддерживает такую возможность,
заставьте его автоматически инициализировать все переменные. Однако, полагаясь на компилятор, вы можете столкнуться с проблемами при переносе кода на другой компьютер или при использовании другого компилятора. Документируйте исполь#
зование параметров компилятора — без такой документации предположения, осно#
ванные на конкретных параметрах компилятора, определить очень трудно.
Внимательно изучайте предупреждения компилятора Многие компиля#
торы предупреждают об использовании неинициализированных переменных.
Проверяйте корректность входных параметров
Это еще один эффективный способ предотвращения ошибок инициализации. Прежде чем присвоить входные значения чему#либо, убедитесь, что они допустимы.
Используйте утилиту проверки доступа к памяти для обнаружения не'
верно инициализированных указателей Некоторые ОС сами следят за кор#
ректностью обращений к памяти, выполняемых при помощи указателей, другие ос#
тавляют вас на произвол судьбы. Тогда можно приобрести инструмент проверки доступа к памяти и проконтролировать использование указателей в своей программе.
Инициализируйте рабочую память при запуске программы Инициализа#
ция рабочей памяти известным значением облегчает поиск ошибок инициализа#
ции. Этого позволяют достичь описанные ниже подходы.

Вы можете использовать специализированную утилиту для заполнения памя#
ти определенным значением перед запуском программы. Для некоторых це#
лей хорошо подходит значение 0, потому что оно гарантирует, что неиници#
ализированные указатели будут указывать на нижнюю область памяти, благо#
даря чему их будет относительно легко найти. В случае процессоров с архи#
тектурой Intel целесообразно заполнить память значением 0xCC, потому что оно соответствует машинному коду команды точки прерывания; если вы запу#
стите код в отладчике и попытаетесь выполнить данные, а не код, вы потонете в точках прерывания. Еще одно достоинство значения 0xCC в том, что его легко заметить в дампах памяти; кроме того, оно редко используется. По этим же причинам Брайан Керниган и Роб Пайк предлагают заполнять память констан#
той 0xDEADBEEF
1
(Kernighan and Pike, 1999).

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

Вы можете сделать так, чтобы программа инициализировала свою рабочую память при запуске. Цель заполнения памяти до запуска программы — обна#
ружение дефектов, тогда как цель этого подхода — их сокрытие. Заполняя рабочую память каждый раз одинаковым значением, вы сможете гарантиро#
вать, что программа не будет зависеть от случайных изменений начальной конфигурации памяти.
Перекрестная ссылка О проверке входных параметров см. главу 8,
преимущественно раздел 8.1.
1
Букв. «мертвая корова». —
Прим. перев.

238
ЧАСТЬ III Переменные
10.4. Область видимости
Область видимости можно понимать как «известность» переменной в програм#
ме. Областью видимости называют фрагмент программы, в котором переменная известна и может быть использована. Переменная с ограниченной или неболь#
шой областью видимости известна только в небольшом фрагменте программы:
в качестве примера можно привести индекс, используемый в теле одного неболь#
шого цикла. Переменная с большой областью видимости известна во многих местах программы: примером может служить таблица с данными о сотрудниках, исполь#
зуемая по всей программе.
В разных языках реализованы разные подходы к области видимости. В некото#
рых примитивных языках все переменные глобальны. В этом случае вы не имее#
те контроля над областью видимости переменных, что создает много проблем.
В C++ и похожих языках переменная может иметь область видимости, соответ#
ствующую блоку (фрагменту кода, заключенному в фигурные скобки), методу, классу
(возможно, и производным от него классам) или всей программе. В Java и C#
переменная может также иметь область видимости, соответствующую пакету или пространству имен (набору классов).
Ниже я привел ряд советов, относящихся к области видимости.
Локализуйте обращения к переменным
Код, расположенный между обращениями к переменной, является «окном уязви#
мости». Чем больше это окно, тем выше вероятность, что в его пределах будет добавлен новый код, искажающий значение переменной, и тем труднее следить за значением переменной при чтении кода. Поэтому обращения к переменной всегда целесообразно локализовать, группируя их вместе.
Идея локализации обращений к переменным самоочевидна, однако она допуска#
ет и формальную оценку. Одним из методов оценки степени сгруппированности обращений к переменной является определение «интервала» (span) между обра#
щениями, например:
Пример определения интервалов между обращениями
к переменным (Java)
a = 0;
b = 0;
c = 0;
a = b + c;
В данном случае между первым и вторым обращениями к
a находятся две строки кода, поэтому и интервал равен 2. Между двумя обращениями к
b — одна строка,
что дает нам интервал, равный 1, ну а интервал между обращениями к
c равен 0.
Вот еще один пример:

ГЛАВА 10 Общие принципы использования переменных
239
Пример интервалов, равных 1 и 0 (Java)
a = 0;
b = 0;
c = 0;
b = a + 1;
b = b / c;
В этом примере между первым и вторым обращениями к
b
одна строка кода, а между вторым и третьим обращениями строк нет, поэтому интервалы равны соответственно 1 и 0.
Средний интервал вычисляется путем усреднения отдельных интервалов. Так, во втором примере средний интервал между обращениями к
b равен (1+0)/2, или 0,5. Локализовав обра#
щения к переменным, вы позволите программисту, который будет читать ваш код,
сосредоточиваться на меньшем фрагменте программы в каждый конкретный мо#
мент времени. Если обращения будут распределены по большему фрагменту кода,
уследить за ними будет сложнее. Таким образом, главное преимущество локали#
зации обращений к переменным в том, что оно облегчает чтение программы.
Делайте время жизни переменных как можно короче
С интервалом между обращениями к переменной тесно связано «время жизни»
переменной — общее число строк, на протяжении которых переменная исполь#
зуется. Жизнь переменной начинается при первом обращении к ней, а заканчи#
вается при последнем.
В отличие от интервала время жизни переменной не зависит от числа обраще#
ний к ней между первым и последним обращениями. Если переменная в первый раз вызывается в строке 1, а в последний — в строке 25, ее время жизни равно 25
строкам. Если переменная используется только в этих двух строках, средний ин#
тервал между обращениями к ней — 23 строки. Если бы между строками 1 и 25
переменная вызывалась в каждой строке, она имела бы средний интервал, равный
0, но время ее жизни по#прежнему равнялось бы 25 строкам. Связь интервалов меж#
ду обращениями к переменной и времени ее жизни пояснена на рис. 10#1.
Как и интервал между обращениями к переменной, время ее жизни желательно делать как можно короче. Преимущество в обоих случаях одинаково: это умень#
шает окно уязвимости, снижая вероятность неверного или неумышленного изме#
нения переменной между действительно нужными обращениями к ней.
Второе преимущество короткого срока жизни: оно позволяет получить верное представление о коде. Если переменная изменяется в строке 10 и вызывается в строке 45, само пространство между двумя обращениями подразумевает, что пе#
ременная используется также между строками 10 и 45. Если переменная изменя#
ется в строке 44 и вызывается в строке 45, других обращений к ней между этими строками быть не может, что позволяет вам сосредоточиться на меньшем фраг#
менте кода.
Дополнительные сведения Об интервалах между обращения- ми к переменным см. работу
«Software Engineering Metrics and Models» (Conte, Dunsmore,
and Shen, 1986).

240
ЧАСТЬ III Переменные
Рис. 10'1. «Длительное время жизни» подразумевает, что переменная используется
в крупном фрагменте кода. При «коротком времени жизни» переменная используется
лишь в небольшом фрагменте. «Интервал между обращениями» к переменной
характеризует, насколько тесно сгруппированы обращения к переменной
Короткое время жизни снижает вероятность ошибок инициализации. По мере изменения программы линейные участки кода имеют тенденцию превращаться в циклы, при этом программисты часто забывают про инициализацию перемен#
ных, выполненную вдали от цикла. Поддерживая код инициализации и код цикла в непосредственной близости, вы снизите вероятность того, что изменения при#
ведут к ошибкам инициализации.
Кроме того, короткое время жизни облегчает чтение кода. Чем меньше строк кода нужно удерживать в уме в каждый конкретный момент времени, тем проще по#
нять код. К тому же при небольшом времени жизни на экране помещаются сразу все обращения к переменной, что облегчает редактирование и отладку.
Наконец, короткое время жизни облегчает разделение крупного метода на мень#
шие. Если обращения к переменным сгруппированы в небольшом фрагменте, его проще выделить в отдельный метод.
Оценка времени жизни переменной
Время жизни переменной можно формализовать, подсчитав число строк между пер#
вым и последним обращениями к ней (с учетом первой и последней строк). В следу#
ющем примере каждая из переменных обладает слишком долгим временем жизни:
Пример слишком долгого времени жизни переменных (Java)
1 // инициализация каждой переменной
2 recordIndex = 0;
3 total = 0;

ГЛАВА 10 Общие принципы использования переменных
241 4 done = false;
26 while ( recordIndex < recordCount ) {
27 ...
Последнее обращение к переменной recordIndex.
28 recordIndex = recordIndex + 1;
64 while ( !done ) {
Последнее обращение к переменной total.
69 if ( total > projectedTotal ) {
Последнее обращение к переменной done.
70 done = true;
Времена жизни переменных:
recordIndex
(строка 28  строка 2 + 1) = 27
total
(строка 69  строка 3 + 1) = 67
done
(строка 70  строка 4 + 1) = 67
Среднее время жизни (27 + 67 + 67) / 3 »54
Следующий пример аналогичен предыдущему, только теперь обращения к пере#
менным сгруппированы более тесно:
Пример хорошего, короткого времени жизни переменных (Java)
Инициализация переменной recordIndex ранее выполнялась в строке 3.
25 recordIndex = 0;
26 while ( recordIndex < recordCount ) {
27 ...
28 recordIndex = recordIndex + 1;
Инициализация переменных total и done ранее выполнялась в строках 4 и 5.
62 total = 0;
63 done = false;
64 while ( !done ) {
69 if ( total > projectedTotal ) {
70 done = true;
Теперь времена жизни переменных равны:
recordIndex
(строка 28  строка 25 + 1) = 4
total
(строка 69  строка 62 + 1) = 8
done
(строка 70  строка 63 + 1) = 8
Среднее время жизни (4 + 8 + 8) / 3 »7
>
>
>
>
>

242
ЧАСТЬ III Переменные
Интуиция подсказывает, что второй вариант предпочтитель#
нее, так как инициализация переменных выполняется бли#
же к месту их использования. Сравнение среднего времени жизни переменных — 54 и 7 — подкрепляет этот интуитив#
ный вывод конкретными цифрами.
Какое время жизни считать приемлемым? А что можно сказать об интервале? Конк#
ретных цифр у нас пока нет, но разумно предположить, что и интервал между обра#
щениями к переменной, и время ее жизни следует пытаться свести к минимуму.
Если в этом ключе проанализировать глобальные переменные, окажется, что им соответствует огромный средний интервал между обращениями и такое же вре#
мя жизни, — это одна из многих обоснованных причин избегать глобальных пе#
ременных.
Общие советы по минимизации области видимости
Ниже даны конкретные рекомендации по минимизации области видимости.
Инициализируйте переменные, используемые в цикле,
непосредственно перед циклом, а не в начале метода,
содержащего цикл Следование этому совету снизит ве#
роятность того, что при изменении цикла вы забудете изме#
нить инициализацию используемых в нем переменных. Если же цикл будет вложен в новый цикл, переменные будут инициализироваться при каждой итерации нового цикла, а не только при первой.
Не присваивайте переменной значение вплоть до его
использования Вероятно, вы знаете, насколько трудно бывает найти строку, в которой переменной было присво#
ено ее значение. Чем больше вы сделаете для прояснения того, где переменная получает свое значение, тем лучше.
Такие языки, как C++ и Java, позволяют инициализировать переменные следующим образом:
Пример грамотного объявления и инициализации переменных (C++)
int receiptIndex = 0;
float dailyReceipts = TodaysReceipts();
double totalReceipts = TotalReceipts( dailyReceipts );
Группируйте связанные команды В следующих фраг#
ментах на примере метода, суммирующего дневную выручку,
показано, как сгруппировать обращения к переменным, что#
бы за ними было проще следить. В первом примере этот принцип нарушен:
Пример запутанного использования двух наборов переменных (C++)
void SummarizeData(...) {
Перекрестная ссылка Об иници- ализации переменных около места их использования см.
раздел 10.3.
Перекрестная ссылка Об этом стиле объявления и определе- ния переменных см. подраздел
«В идеальном случае сразу объявляйте и определяйте каж- дую переменную непосредствен- но перед первым обращением к ней» раздела 10.3.
Перекрестная ссылка О группи- ровке связанных команд см.
также раздел 14.2.
Дополнительные сведения О
времени жизни переменных см.
работу «Software Engineering Met- rics and Models» (Conte, Duns- more, and Shen, 1986).

1   ...   28   29   30   31   32   33   34   35   ...   106


написать администратору сайта