Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по
Скачать 5.88 Mb.
|
ГЛАВА 10 Общие принципы использования переменных 251 Осуществляется ли правильная повторная инициализация переменных в коде, который выполняется более одного раза? Код компилируется без предупреждений? (И задали ли вы самый строгий уровень диагностики?) Если язык поддерживает неявные объявления переменных, постарались ли вы предотвратить возможные проблемы? Другие общие вопросы использования данных Все ли переменные имеют как можно меньшую область видимости? Являются ли обращения к переменным максимально сгруппированными как в плане интервала между обращениями, так и в плане общего времени жизни? Соответствуют ли управляющие структуры типам данных? Все ли объявленные переменные используются? Все ли переменные связываются в подходящее время, т. е. соблюдаете ли вы разумный баланс между гибкостью позднего связывания и соответству- ющей ему повышенной сложностью? Каждая ли переменная имеет одну и только одну цель? Не имеют ли какие-нибудь переменные скрытого смысла? Ключевые моменты Неграмотная инициализация данных часто приводит к ошибкам. Описанные в этой главе способы инициализации позволят избежать проблем, связанных с неожиданными первоначальными значениями переменных. Минимизируйте область видимости каждой переменной. Группируйте обраще# ния к переменным. Старайтесь делать переменные локальными для методов или классов. Избегайте глобальных данных. Располагайте команды, использующие одни и те же переменные, как можно ближе друг к другу. Раннее связывание ограничивает гибкость, но минимизирует сложность про# граммы. Позднее связывание повышает гибкость, но за это приходится распла# чиваться повышенной сложностью. Используйте каждую переменную исключительно с одной целью. 252 ЧАСТЬ III Переменные Г Л А В А 1 1 Сила имен переменных Содержание 11.1. Общие принципы выбора имен переменных 11.2. Именование конкретных типов данных 11.3. Сила конвенций именования 11.4. Неформальные конвенции именования 11.5. Стандартизированные префиксы 11.6. Грамотное сокращение имен переменных 11.7. Имена, которых следует избегать Связанные темы Имена методов: раздел 7.3 Имена классов: раздел 6.2 Общие принципы использования переменных: глава 10 Размещение объявлений данных: одноименный подраздел раздела 31.5 Документирование переменных: подраздел «Комментирование объявлений данных» раздела 32.5 Несмотря на всю важность выбора удачных имен для эффективного программи# рования, я не знаю ни одной книги, в которой эта тема обсуждается хотя бы с ми# нимально приемлемым уровнем детальности. Многие авторы посвящают пару абзацев выбору аббревиатур, приводят несколько банальных примеров и ожида# ют, что вы сами о себе позаботитесь. Я рискую быть обвиненным в противопо# ложном: вы получите настолько подробные сведения об именовании переменных, что никогда не сможете использовать их в полном объеме! Советы, рассматриваемые в этой главе, касаются преимущественно именования переменных: объектов и элементарных типов данных. Однако их следует учиты# вать и при именовании классов, пакетов, файлов и других сущностей из мира программирования. Об именовании методов см. также раздел 7.3. http://cc2e.com/1184 ГЛАВА 11 Сила имен переменных 253 11.1. Общие принципы выбора имен переменных Имя переменной нельзя выбирать, как кличку собаке: опираясь на его вычурность или звучание. В отличие от собаки и ее клички, которые являются разными сущ# ностями, переменная и ее имя формируют по идее одну сущность. Поэтому и адек# ватность переменной во многом определяется ее именем. Выбирайте имена пе# ременных со всей тщательностью. В следующем примере переменные названы плохо: Пример неудачного именования переменных (Java) x = x xx; xxx = fido + SalesTax( fido ); x = x + LateFee( x1, x ) + xxx; x = x + Interest( x1, x ); Что происходит в этом фрагменте кода? Что означают имена x1, xx и xxx? А fido? Допустим, кто#то сказал вам, что этот код подсчитывает общую сумму предъявля# емого клиенту счета, опираясь на его долг и стоимость новых покупок. Какую переменную вы использовали бы для распечатки общей стоимости только новых покупок? Взглянув на исправленный вариант того же кода, ответить на этот вопрос куда проще: Пример удачного именования переменных (Java) balance = balance lastPayment; monthlyTotal = newPurchases + SalesTax( newPurchases ); balance = balance + LateFee( customerID, balance ) + monthlyTotal; balance = balance + Interest( customerID, balance ); Из сравнения этих фрагментов можно сделать вывод, что хорошее имя перемен# ной адекватно ее характеризует, легко читается и хорошо запоминается. Чтобы облегчить себе достижение этих целей, соблюдайте несколько общих правил. Самый важный принцип именования переменных Важнейший принцип именования переменных состоит в том, что имя должно полно и точно описывать сущность, представляемую перемен# ной. Один эффективный способ выбора хорошего имени предполагает формулирование сути переменной в словах. Оптимальным именем переменной часто оказывается само это высказывание. Благодаря отсутствию загадочных со# кращений оно удобочитаемо; к тому же оно однозначно. Так как оно является полным описанием сущности, его нельзя спутать с чем#либо другим. Наконец, такое имя легко запомнить, потому что оно похоже на исходную концепцию. Переменную, представляющую число членов олимпийской команды США, мож# но было бы назвать numberOfPeopleOnTheUsOlympicTeam. Переменную, представ# 254 ЧАСТЬ III Переменные ляющую число мест на стадионе, — numberOfSeatsInTheStadium. Для хранения максимального числа очков, набранных спортсменами какой#то страны в совре# менной Олимпиаде, можно было бы создать переменную maximumNumberOfPoint% sInModernOlympics. Переменную, определяющую текущую процентную ставку, лучше было бы назвать rate или interestRate, а не r или x. Думаю, идею вы поняли. Обратите внимание на две характеристики этих имен. Во#первых, их легко рас# шифровать. Фактически их не нужно расшифровывать вообще: их можно просто прочитать. Ну, а во#вторых, некоторые имена велики — слишком велики, чтобы быть практичными. Длину имен переменных мы рассмотрим ниже. Несколько примеров удачных и неудачных имен переменных я привел в табл. 11#1. Табл. 11-1. Примеры удачных и неудачных имен переменных Удачные имена, Неудачные имена, Суть переменной адекватное описание неадекватное описание Сумма, на которую runningTotal, checkTotal written, ct, checks, CHKTTL, на данный момент x, x1, x2 выписаны чеки Скорость поезда velocity, trainVelocity, velocityInMph velt, v, tv, x, x1, x2, train Текущая дата currentDate, todaysDate cd, current, c, x, x1, x2, date Число строк на странице linesPerPage lpp, lines, l, x, x1, x2 Имена currentDate и todaysDate — хорошие имена, потому что полно и точно описывают идею «текущей даты». Фактически они составлены из слов с очевид# ным значением. Программисты иногда упускают из виду обычные слова, которые порой приводят к самому простому решению. Имена cd и c неудачны потому, что слишком коротки и «неописательны». Имя current тоже неудачно: оно не говорит, что именно является «текущим». Имя date кажется хорошим, но в итоге оно ока# зывается плохим, потому что мы имеем в виду не любую дату, а текущую; само по себе имя date об этом не говорит. Имена x, x1 и x2 заведомо неудачны: x тради# ционно представляет неизвестное количество чего#либо, и, если вы не хотите, чтобы ваши переменные были неизвестными величинами, подумайте о выборе других имен. Имена должны быть максимально конкретны. Имена x, temp, i и другие, достаточно общие для того, чтобы их можно было использовать более чем с одной целью, не так информативны, как могли бы быть, и обычно являются плохими. Ориентация на проблему Хорошее мнемоническое имя чаще всего описывает проблему, а не ее решение. Хорошее имя в большей степени выражает что, а не как. Если же имя описывает некоторый аспект вычислений, а не проблемы, имеет место обратное. Предпочи# тайте таким именам переменных имена, характеризующие саму проблему. Запись данных о сотруднике можно было бы назвать inputRec или employeeData. Имя inputRec — компьютерный термин, выражающий идеи ввода данных и запи# си. Имя employeeData относится к проблемной области, а не к миру компьюте# ров. В случае битового поля, определяющего статус принтера, имя bitFlag более ГЛАВА 11 Сила имен переменных 255 компьютеризировано, чем printerReady, а в случае приложения бухгалтерского учета calcVal более компьютеризировано, чем sum. Оптимальная длина имени переменной Оптимальная длина имени, наверное, лежит где#то между длинами имен x и maxi% mumNumberOfPointsInModernOlympics. Слишком короткие страдают от недостат# ка смысла. Проблема с именами вроде x1 и x2 в том, что, даже узнав, что такое x, вы ничего не сможете сказать об отношении между x1 и x2. Слишком длинные имена надоедает печатать, к тому же они могут сделать неясной визуальную струк# туру программы. Горла, Бенандер и Бенандер обнаружили, что отладка программы требо# вала меньше всего усилий, если имена переменных состояли в среднем из 10–16 символов (Gorla, Benander, and Benander, 1990). Отладка про# грамм с именами, состоящими в среднем из 8–20 символов, была почти столь же легкой. Это не значит, что следует присваивать всем переменным имена из 9–15 или 10–16 символов, — это значит, что, увидев в своем коде много более корот# ких имен, вы должны проверить их ясность. Вопрос адекватности длины имен переменных поясняет табл. 11#2. Табл. 11-2. Слишком длинные, слишком короткие и оптимальные имена переменных Слишком длинные имена: numberOfPeopleOnTheUsOlympicTeam numberOfSeatsInTheStadium maximumNumberOfPointsInModernOlympics Слишком короткие имена: n, np, ntm n, ns, nsisd m, mp, max, points То, что надо: numTeamMembers, teamMemberCount numSeatsInStadium, seatCount teamPointsMax, pointsRecord Имена переменных и область видимости Всегда ли короткие имена переменных неудачны? Нет, не всегда. Если вы присваиваете переменной короткое имя, такое как i, сама длина имени говорит о том, что перемен# ная является второстепенной и имеет ограниченную область действия. Программист, читающий код, сможет догадаться, что использование такой пере# менной ограничивается несколькими строками кода. Присваивая переменной имя i, вы говорите: «Эта переменная — самый обычный счетчик цикла/индекс масси# ва, не играющий никакой роли вне этих нескольких строк». У. Дж. Хансен (W. J. Hansen) обнаружил, что более длинные имена лучше присва# ивать редко используемым или глобальным переменным, а более короткие — локальным переменным или переменным, вызываемым в циклах (Shneiderman, 1980). Однако с короткими именами связано много проблем, и некоторые осмот# Перекрестная ссылка Об обла- сти видимости см. раздел 10.4. 256 ЧАСТЬ III Переменные рительные программисты, придерживающиеся политики защитного программи# рования, вообще избегают их. Дополняйте имена, относящиеся к глобальному пространству имен, спе' цификаторами Если у вас есть переменные, относящиеся к глобальному про# странству имен (именованные константы, имена классов и т. д.), подумайте, при# нять ли конвенцию, разделяющую глобальное пространство имен на части и пре# дотвращающую конфликты имен. В C++ и C# для разделения глобального простран# ства имен можно применить ключевое слово namespace: Пример разделения глобального пространства имен с помощью ключевого слова namespace (C++) namespace UserInterfaceSubsystem { // объявления переменных } namespace DatabaseSubsystem { // объявления переменных } Если класс Employee объявлен в обоих пространствах имен, вы можете указать нужное пространство имен, написав UserInterfaceSubsystem::Employee или Data% baseSubsystem::Employee. В Java с той же целью можно использовать пакеты. Программируя на языке, не поддерживающем пространства имен или пакеты, вы все же можете принять конвенции именования для разделения глобального про# странства имен. Скажем, вы можете дополнить глобальные классы префиксами, определяющими подсистему. Класс Employee из подсистемы пользовательского интерфейса можно было бы назвать uiEmployee, а тот же класс из подсистемы доступа к БД — dbEmployee. Это позволило бы свести к минимуму риск конфлик# тов в глобальном пространстве имен. Спецификаторы вычисляемых значений Многие программы включают переменные, содержащие вычисляемые значения: суммы, средние величины, максимумы и т. д. Дополняя такое имя спецификатором вроде Total, Sum, Average, Max, Min, Record, String или Pointer, укажите его в конце имени. У такого подхода несколько достоинств. Во#первых, при этом самая значимая часть имени переменной, определяющая наибольшую часть его смысла, располагается в самом начале имени, из#за чего становится более заметной и читается первой. Во#вторых, приняв эту конвенцию, вы предотвратите путаницу, возможную при наличии в одной программе имен totalRevenue и revenueTotal. Эти имена семан# тически эквивалентны, и конвенция не позволила бы использовать их как разные. В#третьих, набор имен вроде revenueTotal, expenseTotal, revenueAverage и expen% seAverage обладает приятной глазу симметрией, тогда как набор имен totalRevenue, ГЛАВА 11 Сила имен переменных 257 expenseTotal, revenueAverage и averageExpense упорядоченным не кажется. Наконец, согласованность имен облегчает чтение и сопровождение программы. Исключение из этого правила — позиция спецификатора Num. При расположе# нии в начале имени спецификатор Num обозначает общее число: например, num% Customers — это общее число заказчиков. Если же он указан в конце имени, то определяет индекс: так, customerNum — это номер текущего заказчика. Другим признаком данного различия является буква s в конце имени numCustomers. Од# нако даже в этом случае спецификатор Num очень часто приводит к замешатель# ству, поэтому лучше всего полностью исключить проблему, применив Count или Total для обозначения общего числа заказчиков и Index для ссылки на конкретно# го заказчика. Таким образом, переменные, определяющие общее число заказчи# ков и номер конкретного заказчика, получили бы имена customerCount и customer% Index соответственно. Антонимы, часто встречающиеся в именах переменных Используйте антонимы последовательно. Это делает код бо# лее согласованным и облегчает его чтение. Пары вроде begin/ end понять и запомнить легко. Пары, не соответствующие общепринятым антонимам, запоминаются сложнее и вызы# вают замешательство. В число антонимов, часто используе# мых в именах переменных, входят: begin/end; first/last; locked/unlocked; min/max; next/previous; old/new; opened/closed; visible/invisible; source/target; source/destination; up/down. 11.2. Именование конкретных типов данных При именовании конкретных типов данных следует руководствоваться не толь# ко общими, но и специфическими соображениями. Ниже описаны принципы именования индексов циклов, переменных статуса, временных переменных, бу# левых переменных, перечислений и именованных констант. Именование индексов циклов Принципы именования индексов циклов возникли потому, что циклы относятся к самым популярным конструкциям. Как правило, в качестве индексов циклов используют пере# менные i, j и k: Перекрестная ссылка Аналогич- ный список антонимов, исполь- зуемых в именах методов, см. в подразделе «Дисциплиниро- ванно используйте антонимы» раздела 7.3. Перекрестная ссылка О циклах см. главу 16. 258 ЧАСТЬ III Переменные Пример простого имени индекса цикла (Java) for ( i = firstItem; i < lastItem; i++ ) { data[ i ] = 0; } Если же переменную предполагается использовать вне цикла, ей следует присво# ить более выразительное имя. Например, переменную, хранящую число записей, прочитанных из файла, можно было бы назвать recordCount: Пример удачного описательного имени индекса цикла (Java) recordCount = 0; while ( moreScores() ) { score[ recordCount ] = GetNextScore(); recordCount++; } // строки, в которых используется переменная recordCount Если цикл длиннее нескольких строк, смысл переменной i легко забыть, поэтому в подобной ситуации лучше присвоить индексу цикла более выразительное имя. Так как код очень часто приходится изменять, модернизировать и копировать в другие программы, многие опытные программисты вообще не используют име# на вроде i. Одна из частых причин увеличения циклов — их вложение в другие циклы. Если у вас есть несколько вложенных циклов, присвойте индексам более длинные имена, чтобы сделать код более понятным: Пример удачного именования индексов вложенных циклов (Java) for ( teamIndex = 0; teamIndex < teamCount; teamIndex++ ) { for ( eventIndex = 0; eventIndex < eventCount[ teamIndex ]; eventIndex++ ) { score[ teamIndex ][ eventIndex ] = 0; } } Тщательный выбор имен индексов циклов позволяет избежать путаницы индек# сов — использования i вместо j и наоборот. Кроме того, это облегчает понима# ние операций над массивами: команда score[ teamIndex ][ eventIndex ] более ин# формативна, чем score[ i ][ j ]. Не присваивайте имена i, j и k ничему, кроме индексов простых циклов: наруше# ние этой традиции только запутает других программистов. Чтобы избежать по# добных проблем, просто подумайте о более описательных именах, чем i, j и k. Именование переменных статуса Переменные статуса характеризуют состояние программы. Ниже рассмотрен один принцип их именования. |