Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 881
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
1 ... 33 34 35 36 37 38 39 40 ... 104
ГЛАВА 12 Основные типы данных
293
Пример логического условия, чье назначение понятно (Java)
finished = ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) );
repeatedEntry = ( elementIndex == lastElementIndex );
if ( finished || repeatedEntry ) {
}
Используйте логические переменные для упрощения сложных условий
Чтобы правильно закодировать сложное условие, часто приходится делать несколько попыток. Когда через некоторое время нужно модифицировать это условие, быва- ет сложно разобраться, что же оно проверяет. Логические переменные могут уп- ростить проверку. В предыдущем примере программа на самом деле проверяет два условия: завершено ли выполнение метода и выполняется ли этот метод повторно.
Создав логические переменные
finished и repeatedEntry, вы упрощаете if-проверку:
теперь ее легче читать, легче изменять, и она меньше подвержена ошибкам.
Вот другой пример сложного условия:
Пример сложного условия (Visual Basic)
If ( ( document.AtEndOfStream() ) And ( Not inputError ) ) And _
( ( MIN_LINES <= lineCount ) And ( lineCount <= MAX_LINES ) ) And _
( Not ErrorProcessing() ) Then
‘ делаем что-то
End If
Условие в примере достаточно запутанное, но все же часто встречающееся. Оно налагает на читателя тяжелую умственную нагрузку. Могу предположить, что вы даже не попытаетесь разобраться в этой
if-проверке, а посмотрите и скажете: «Раз- берусь с этим позже, если и впрямь понадобится». Обратите внимание на эту мысль,
ведь это абсолютно то же самое, что сделают другие люди, читая ваш код, содер- жащий подобные условия.
А вот как переписать этот код, используя логические переменные, добавленные для упрощения условия:
Пример упрощенного условия (Visual Basic)
allDataRead = ( document.AtEndOfStream() ) And ( Not inputError )
legalLineCount = ( MIN_LINES <= lineCount ) And ( lineCount <= MAX_LINES )
Вот упрощенная проверка.
If ( allDataRead ) And ( legalLineCount ) And ( Not ErrorProcessing() ) Then
‘ делаем что-то
End If
Вторая версия проще. Думаю, вы легко прочитаете логическое выражение в
if- проверке.
>
294
ЧАСТЬ III Переменные
Создайте свой логический тип в случае необходимости Некоторые язы- ки, такие как C++, Java и Visual Basic, имеют предопределенный логический тип,
другие — скажем, C — не имеют. В языках, подобных C, вы можете определить свой логический тип. В C это можно сделать так:
Пример определения типа BOOLEAN
с помощью обычного typedef на языке C
typedef int BOOLEAN;
Или можно это сделать, определив дополнительно значения
true и false:
Пример определения типа Boolean с помощью Enum на языке C
enum Boolean {
True=1,
False=(!True)
};
Объявление переменных как
BOOLEAN, а не int делает их последующее использо- вание более очевидным, а вашу программу — самодокументируемой.
12.6. Перечислимые типы
Перечислимым называется тип данных, который позволяет описать на естествен- ном языке каждый элемент класса или объекта. Перечислимые типы реализова- ны в C++ и Visual Basic и обычно используются, когда вы знаете все возможные значения переменной и хотите выразить их словами. Вот примеры перечисли- мых типов на Visual Basic:
Примеры перечислимых типов (Visual Basic)
Public Enum Color
Color_Red
Color_Green
Color_Blue
End Enum
Public Enum Country
Country_China
Country_England
Country_France
Country_Germany
Country_India
Country_Japan
Country_Usa
End Enum
Public Enum Output
Output_Screen
Output_Printer
ГЛАВА 12 Основные типы данных
295
Output_File
End Enum
Перечислимые типы — мощная альтернатива устаревшим схемам, в которых вы подробно расписываете: «1 — это красный, 2 — зеленый, 3 — голубой…». В связи с этим можно предложить следующие принципы применения таких типов.
Используйте перечислимые типы для читабельности Вместо выражений вроде:
if chosenColor = 1
вы можете написать более читабельную фразу, например:
if chosenColor = Color_Red
Каждый раз, когда вы видите числовую константу, подумайте: не заменить ли ее перечислимым типом.
Перечислимые типы особенно полезны для определения параметров методов. Кто поймет, что означают параметры этой функции?
Пример вызова функции, в котором стоило бы
использовать перечислимые типы (C++)
int result = RetrievePayrollData( data, true, false, false, true );
А параметры в этом вызове функции гораздо понятнее:
Пример вызова функции, использующей
перечислимые типы для читабельности (C++)
int result = RetrievePayrollData(
data,
EmploymentStatus_CurrentEmployee,
PayrollType_Salaried,
SavingsPlan_NoDeduction,
MedicalCoverage_IncludeDependents
);
Используйте перечислимые типы для надежности В некоторых языках (осо- бенно в Ada) перечислимые типы заставляют компилятор выполнять более тщатель- ную проверку типов, чем при работе с целыми значениями и константами. При использовании именованных констант компилятору неоткуда узнать, что единствен- ные возможные значения переменной — это
Color_Red, Color_Green и Color_Blue.
Компилятор не будет возражать, встретив выражения вроде
color = Country_England
или
country = Output_Printer. Если вы используете перечислимые типы, то при объяв- лении переменной типа
Color компилятор позволит присвоить ей только значе- ния
Color_Red, Color_Green или Color_Blue.
Используйте перечислимые типы для модифицируемости Перечислимые типы позволяют вам легко модифицировать код. Обнаружив пробел в своей схе- ме «1 — это красный, 2 — зеленый, 3 — голубой», придется пройти по коду и из- менить все вхождения
1, 2, 3 и т. д. А используя перечислимые типы, вы можете
296
ЧАСТЬ III Переменные продолжить добавление элементов в список, просто поместив их в определение типа и перекомпилировав программу.
Используйте перечислимые типы как альтернативу логическим пере-
менным Очень часто логической переменной недостаточно для представления необходимых значений. Представьте, что у вас есть метод, возвращающий
True,
если он успешно выполнил свою задачу, и
False в противном случае. Позднее вы можете обнаружить, что на самом деле у вас есть два варианта
False. Первый оз- начает, что задание не выполнено, но это повлияло только на сам метод; второй —
что невыполнение задания привело к фатальной ошибке, которую надо передать в остальную часть программы. В этом случае перечислимый тип со значениями
Status_Success, Status_Warning и Status_FatalError подходит больше, чем логический тип со значениями
true и false. Эту схему очень легко расширять новыми вариан- тами успешного или неудачного выполнения.
Проверяйте некорректные значения Когда вы используете перечислимый тип в условиях
if или case, проверяйте появление недопустимых значений. В переклю- чателе
case для перехвата неправильных значений применяется оператор else:
Хороший пример проверки некорректных значений
в перечислимом типе (Visual Basic)
Select Case screenColor
Case Color_Red
Case Color_Blue
Case Color_Green
Здесь выполняется проверка неправильного значения.
Case Else
DisplayInternalError( False, “Internal Error 752: Invalid color.” )
End Select
Настройте первый и последний элемент перечислимого типа для исполь-
зования в качестве границ циклов Определение первого и последнего эле- мента в перечислении в виде
Color_First, Color_Last, Country_First, Country_Last и т. д., позволяет писать циклы, проходящие по всем элементам типа. Вы настраи- ваете свой перечислимый тип, используя явные значения:
Пример установки значений First и Last перечислимого типа (Visual Basic)
Public Enum Country
Country_First = 0
Country_China = 0
Country_England = 1
Country_France = 2
Country_Germany = 3
Country_India = 4
Country_Japan = 5
>
ГЛАВА 12 Основные типы данных
297
Country_Usa = 6
Country_Last = 6
End Enum
Теперь элементы
Country_First и Country_Last могут служить как границы цикла:
Хороший пример циклического вызова элементов перечисления (Visual Basic)
‘ Вычисляем курсы валют по отношению к долларам США.
Dim usaCurrencyConversionRate( Country_Last ) As Single
Dim iCountry As Country
For iCountry = Country_First To Country_Last usaCurrencyConversionRate( iCountry ) = ConversionRate( Country_Usa, iCountry )
Next
Зарезервируйте первый элемент перечислимого типа как недопустимый
При объявлении перечислимого типа зарезервируйте его первый элемент в ка- честве недопустимого значения. Большинство компиляторов присваивает перво- му элементу перечисления значение
0. Объявление некорректным элемента, при- равненного
0, поможет выявить неправильно проинициализированные перемен- ные, так как они вероятнее всего будут равны
0, чем любому другому неправиль- ному значению.
Вот как при этом подходе будет выглядеть перечислимый тип
Country:
Пример объявления первого элемента перечисления недопустимым (Visual Basic)
Public Enum Country
Country_InvalidFirst = 0
Country_First = 1
Country_China = 1
Country_England = 2
Country_France = 3
Country_Germany = 4
Country_India = 5
Country_Japan = 6
Country_Usa = 7
Country_Last = 7
End Enum
Точно укажите в стандартах кодирования для проекта, как должны ис-
пользоваться первый и последний элементы, и неукоснительно придержи-
вайтесь этого Использование в перечислениях элементов InvalidFirst, First и
Last может сделать объявления массивов и операторы циклов читабельнее. Но есть вероятность неразберихи в таких вопросах: с чего начинаются значимые элементы перечисления — с
0 или 1 и является ли допустимыми первый и последний эле- менты? Если используется эта технология, стандарты кодирования для проекта в целях уменьшения количества ошибок должны требовать применения элементов
InvalidFirst, First и Last для всех без исключения перечислений.
298
ЧАСТЬ III Переменные
Помните о подводных камнях в присваивании явных значений элементам
перечисления Некоторые языки позволяют присваивать конкретные значения элементам перечисления:
Пример явного присваивания значений элементам перечисления (C++)
enum Color {
Color_InvalidFirst = 0,
Color_First = 1,
Color_Red = 1,
Color_Green = 2,
Color_Blue = 4,
Color_Black = 8,
Color_Last = 8
};
Если в этом примере вы объявите переменную цикла типа
Color и попробуете пройти по всем элементам
Color, то этой переменной наряду с допустимыми зна- чениями 1, 2, 4 и 8 будут присваиваться и недопустимые — 3, 5, 6 и 7.
Если ваш язык не поддерживает
перечислимые типы
Если в вашем языке нет перечислимых типов, их можно имитировать, используя глобальные переменные или клас- сы. Например, такие объявления можно использовать в языке
Java:
Пример имитации перечислимых типов (Java)
// Создание перечислимого типа Country.
class Country {
private Country() {}
public static final Country China = new Country();
public static final Country England = new Country();
public static final Country France = new Country();
public static final Country Germany = new Country();
public static final Country India = new Country();
public static final Country Japan = new Country();
}
// Создание перечислимого типа Output.
class Output {
private Output() {}
public static final Output Screen = new Output();
public static final Output Printer = new Output();
public static final Output File = new Output();
}
Перекрестная ссылка На момент написания этой книги Java не поддерживает перечислимых ти- пов. Но, возможно, уже делает это к моменту, когда вы это чи- таете.
ГЛАВА 12 Основные типы данных
299
Такие перечислимые типы делают вашу программу читабельнее, поскольку вы можете использовать открытые члены класса, например,
Country. England и Out-
put. Screen, вместо именованных констант. Именно такой способ создания пере- числимых типов еще и обеспечивает безопасность типов, так как каждый тип объяв- лен как класс и компилятор будет проверять некорректные присваивания вроде
Output output = Country. England (Bloch, 2001).
В языках, не поддерживающих классы, такого же эффекта можно достичь, акку- ратно используя глобальные переменные для каждого элемента перечисления.
12.7. Именованные константы
Именованные константы аналогичны переменным за исключением того, что вы не можете изменить значение константы после ее инициализации. Именованные кон- станты позволяют ссылаться на постоянные величины, например, максимальное количество работников, используя имя, а не число —
MAXIMUM_EMPLOYEES, а не 1000.
Применение именованных констант — это способ «параметризации» программы,
т. е. размещение некоторой характеристики, которая может измениться, в пара- метре. В результате вам потребуется изменить его только в одном месте, а не по всей программе. Если вы когда-нибудь объявляли массив такой длины, какой, по- вашему, должно хватить для любых ситуаций, а потом выяснялось, что массив слишком мал, вы оцените значение именованных констант. Когда размер масси- ва изменяется, вы меняете только определение константы, используемой для объяв- ления массива. Такое «централизованное управление» имеет большое значение для того, чтобы сделать работу с ПО действительно приятной: упростить его поддер- жку и модификацию.
Используйте именованные константы в объявлениях данных Именован- ные константы повышают читабельность и удобство сопровождения объявлений данных и выражений, которым необходимо знать размеры обрабатываемых дан- ных. В следующем примере для описания длины телефонных номеров работни- ков лучше использовать
LOCAL_NUMBER_LENGTH, а не число 7.
Хороший пример применения именованных констант
в объявлениях данных (Visual Basic)
Const AREA_CODE_LENGTH = 3
LOCAL_NUMBER_LEN GTH здесь объявляется как константа.
Const LOCAL_NUMBER_LENGTH = 7
Type PHONE_NUMBER
areaCode( AREA_CODE_LENGTH ) As String
А здесь используется.
localNumber( LOCAL_NUMBER_LENGTH ) As String
End Type
>
>
300
ЧАСТЬ III Переменные
’ Убедимся, что все символы в телефонном номере — это цифры.
И здесь тоже используется.
For iDigit = 1 To LOCAL_NUMBER_LENGTH
If ( phoneNumber.localNumber( iDigit ) < “0” ) Or _
( “9” < phoneNumber.localNumber( iDigit ) ) Then
‘ Выполняем обработку ошибок.
Это простой пример, но вы вполне можете представить программу, в которой сведения о длине телефонных номеров требуются во многих местах.
На момент создания программы все работники живут в одной стране, поэтому вам нужно только семь цифр для их телефонных номеров. По мере расширения ком- пании ее филиалы открываются в разных странах, и вам понадобятся более длин- ные телефонные номера. Если вы параметризовали эту длину, вам надо сделать изменение только в одном месте — в определении именованной константы
LOCAL_
NUMBER_LENGTH.
Как вы, наверное, поняли, именованные константы делают сопровождение программы удобнее. Как правило, любая технология, централизующая управление объектами, подвер- женными изменениям, — это хороший способ уменьшить затраты на сопровождение (Glass, 1991).
Избегайте литеральных значений, даже «безопасных» Как вы думаете, что в следующем цикле означает число
12?
Пример непонятного кода (Visual Basic)
For i = 1 To 12
profit( i ) = revenue( i ) – expense( i )
Next
Исходя из специфического содержимого кода, можно предположить, что выпол- няется цикл по 12 месяцам в году. Но вы
уверены? Вы поставите на это свое со- брание «Монти Пайтон»?
В этом случае вам не нужно использовать именованные константы для поддерж- ки расширяемости: вряд ли число месяцев в году изменится в ближайшем буду- щем. Но если при написании кода остается хотя бы тень сомнения в его предназ- начении, развейте ее с помощью хорошо названной именованной константы,
например, так:
Пример более понятного кода (Visual Basic)
For i = 1 To NUM_MONTHS_IN_YEAR
profit( i ) = revenue( i ) – expense( i )
Next
Это уже лучше, но для завершения примера индекс цикла тоже нужно назвать более информативно:
Дополнительные сведения О зна- чении централизованного управ- ления см. стр. 57–60 в книге
«Software Conflict» (Glass, 1991).
>