Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 879
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА 12 Основные типы данных
301
Пример еще более понятного кода (Visual Basic)
For month = 1 To NUM_MONTHS_IN_YEAR
profit( month ) = revenue( month ) – expense( month )
Next
Этот пример выглядит весьма неплохо, но мы можем сделать еще один шаг впе- ред, применив перечислимый тип:
Пример очень понятного кода (Visual Basic)
For month = Month_January To Month_December profit( month ) = revenue( month ) – expense( month )
Next
В последнем примере не может возникнуть никаких сомнений относительно назначения цикла. Даже если вы считаете, что литеральное значение безопасно,
используйте вместо него именованную константу. Фанатично искорените лите- ралы из вашего кода. С помощью текстового редактора выполните поиск
2, 3, 4, 5,
6, 7, 8 и 9, чтобы убедиться, что вы не используете их случайно.
Имитируйте именованные константы с помощью
переменных или классов правильной области види-
мости Если ваш язык не поддерживает именованные кон- станты, их можно создать. Подход, аналогичный приведен- ному выше Java-примеру, имитирующему перечислимые типы, позволяет получить преимущества использования именованных констант. Старайтесь применять обычные правила области види- мости: отдавайте предпочтение локальной, классовой или глобальной области видимости именно в таком порядке.
Последовательно используйте именованные константы Опасно исполь- зовать для представления одной сущности именованные константы в одном мес- те и литералы в другом. Некоторые приемы программирования напрашиваются на ошибки, а этот просто доставляет вам ошибки на дом. Если значение имено- ванной константы нужно изменить, вы сделаете это и подумаете, что выполнили все необходимые изменения. Вы не обратите внимания на жестко закодирован- ные литералы, и ваша программа будет демонстрировать таинственные дефекты.
Их устранение может потребовать так много усилий, что захочется схватить те- лефонную трубку и молить о помощи.
12.8. Массивы
Массивы — простейшие и наиболее часто используемые типы структурирован- ных данных. В некоторых языках это единственный вид структурированных дан- ных. Массивы состоят из группы элементов одинакового типа, доступ к которым осуществляется напрямую по индексу.
Убедитесь, что все значения индексов массива не выходят за его
границы Все проблемы с массивами так или иначе связаны с тем, что доступ к их элементам может осуществляться произвольно. Наиболее часто
Перекрестная ссылка Об ими- тации перечислимых типов см.
подраздел «Если ваш язык не поддерживает перечислимые типы» раздела 12.6.
302
ЧАСТЬ III Переменные возникающая проблема объясняется попыткой доступа к элементу по индексу, вы- ходящему за пределы массива. В некоторых языках при этом генерируется ошиб- ка, а в других — получаются причудливые и неожиданные результаты.
Обдумайте применение контейнеров вместо массивов или рассматривай-
те массивы как последовательные структуры Некоторые именитые в ком- пьютерной науке люди предлагали запретить произвольный доступ к массиву,
заменив его последовательным (Mills and Linger, 1986). Аргументтруют они это тем,
что произвольный доступ к массиву похож на случайные операторы
goto в про- грамме: их применение приводит к неаккуратному, подверженному ошибкам коду,
в корректности которого сложно быть уверенным. Поэтому вместо массивов пред- лагается использовать множества, стеки и очереди, доступ к элементам которых выполняется последовательно.
Проведя небольшой эксперимент, Миллз (Mills) и Линджер (Linger) вы- яснили, что разработанный таким образом проект потребовал исполь- зования меньшего числа переменных и меньшего числа ссылок на эти переменные. То есть проект был относительно эффективнее, что привело к со- зданию более надежного ПО.
Рассмотрите вопрос использования контейнерных классов с последовательным доступом — наборов, стеков, очередей и т. п. — как альтернативу прежде, чем выбрать массив.
Проверяйте конечные точки массивов Как бывает по- лезно продумать применение конечных точек в операторе цикла, так и вы сможете обнаружить немало ошибок, прове- рив крайние элементы массивов. Задайтесь вопросом, пра- вильно ли выполняется доступ к первому элементу массива или случайно используется элемент перед ним либо после него. А что с последним элементом? Нет ли в коде ошибки потери единицы? И, наконец, спросите себя, пра- вильно ли код обращается к элементам в середине массива.
В многомерном массиве убедитесь, что его индексы используются в пра-
вильном порядке Очень легко написать Array[ i ][ j ], имея в виду Array[ j ][ i ],
так что не жалейте времени для проверки правильного порядка индексов. Попро- буйте использовать более значимые имена, чем
i и j, когда их назначение не вполне очевидно.
Остерегайтесь пересечения индексов При использовании вложенных цик- лов легко написать
Array[ j ], имея в виду Array[ i ]. Перемена мест индексов назы- вается «пересечением индексов» (index cross-talk). Проверьте эту возможность.
Опять же, используйте более значимые имена индексов, чем
i и j, чтобы ошибки пересечения изначально сложнее было совершить.
В языке C для работы с массивами используйте макрос ARRAY_LENGTH()
Вы можете добавить гибкости вашей работе с массивами, определив макрос
ARRAY_
LENGTH():
Пример определения макроса ARRAY_LENGTH() на языке C
#define ARRAY_LENGTH( x ) (sizeof(x)/sizeof(x[0]))
Перекрестная ссылка Вопросы применения массивов и циклов имеют много общего. Подроб- нее о циклах см. главу 16.
1 ... 34 35 36 37 38 39 40 41 ... 104
ГЛАВА 12 Основные типы данных
303
При выполнении операций над массивами для указания верхней границы исполь- зуйте макрос
ARRAY_LENGTH() вместо именованной константы. Например:
Пример использования макроса ARRAY_LENGTH()
для операций с массивами на языке C
ConsistencyRatios[] =
{ 0.0, 0.0, 0.58, 0.90, 1.12,
1.24, 1.32, 1.41, 1.45, 1.49,
1.51, 1.48, 1.56, 1.57, 1.59 };
Вот здесь используется макрос.
for ( ratioIdx = 0; ratioIdx < ARRAY_LENGTH( ConsistencyRatios ); ratioIdx++ );
Этот способ особенно полезен для массивов неопределенного размера, как в этом примере. Если вы добавляете или удаляете элементы, вам не надо помнить об изменении именованной константы, определяющей размер массива. Разумеется,
эта технология работает и с массивами заданного размера, но, используя этот подход, вам не всегда надо будет создавать дополнительные именованные кон- станты для объявления массивов.
12.9. Создание собственных
типов данных (псевдонимы)
Типы данных, определяемые программистом, — одна из наиболее мощ- ных возможностей, позволяющих наиболее четко обозначить ваше понимание программы. Они защищают программу от непредвиденных изменений и упрощают ее прочтение, и все это — без необходимости проекти- ровать, разрабатывать или тестировать новые классы. Если вы программируете на
C, C++ или других языках, поддерживающих такие типы, задействуйте это преиму- щество!
Чтобы оценить возможности создания типов, представьте, что вы пишете программу для преобразования координат из сис- темы
x, y, z в широту, долготу и высоту. Вам кажется, что могут потребоваться числа с плавающей запятой двойной точнос- ти, но пока вы абсолютно в этом не уверены, предпочитаете писать программу, используя числа с одинарной точностью. Вы можете создать но- вый тип данных специально для координат, применив оператор
typedef в C или C++
или его эквивалент в другом языке. Вот как вы определите такой тип в C++:
Пример создания типа (C++)
typedef float Coordinate; // для координатных переменных
Это определение объявляет новый тип
Coordinate, функционально идентичный типу
float. Чтобы задействовать этот тип, вы просто объявляете с ним переменные точ- но так же, как и с любым предопределенным типом вроде
float. Пример:
Перекрестная ссылка Во многих случаях лучше создавать класс,
чем простой тип данных. Под- робнее см. главу 6.
>
304
ЧАСТЬ III Переменные
Пример использования созданного типа (C++)
Routine1( ... ) {
Coordinate latitude; // широта в градусах
Coordinate longitude; // долгота в градусах
Coordinate elevation; // высота в метрах от центра Земли
}
Routine2( ... ) {
Coordinate x; // координата x в метрах
Coordinate y; // координата y в метрах
Coordinate z; // координата z в метрах
}
Здесь все переменные
latitude, longitude, elevation, x, y и z объявлены с типом
Coordinate.
Теперь допустим, что программа изменилась и вы выяснили, что все-таки нужны переменные с двойной точностью. Поскольку вы создали тип специально для координатных данных, все, что вам нужно изменить, — это определение типа.
И сделать это вам необходимо только в одном месте — в выражении
typedef. Вот как выглядит новое определение типа:
Пример измененного определения типа (C++)
Первоначальный тип float заменен на double.
typedef double Coordinate; // для координатных переменных
Вот еще один пример — теперь на языке Pascal. Представьте, что вы разрабатыва- ете систему расчета заработной платы, в которой длина имен работников не пре- вышает 30 символов. Пользователи сказали вам, что
ни у кого нет имени длинней
30 символов. Закодируете ли вы число
30 по всей программе? Если да, то вы дове- ряете вашим пользователям гораздо больше, чем я — своим. Лучший подход со- стоит в определении типа для имен работников:
Пример создания типа для имен работников (Pascal)
Type employeeName = array[ 1..30 ] of char;
Когда речь идет о строке или массиве, обычно разумно определить именованную константу, содержащую длину строки или массива, а затем задействовать ее в определении типа. Вы найдете в своей программе много мест, в которых стоит использовать константу, и это — первое из них. Вот как это выглядит:
Пример лучшего создания типа (Pascal)
Const
>
ГЛАВА 12 Основные типы данных
305
Вот объявление именованной константы.
NAME_LENGTH = 30;
Type
Здесь эта именованная константа используется.
employeeName = array[ 1..NAME_LENGTH ] of char;
Еще более усовершенствованный пример может комбинировать идею создания собственных типов с технологией сокрытия информации. Порой сведения, ко- торые вы хотите скрыть, и есть информация о типе данных.
Пример с координатами на C++ частично удовлетворяет принципу сокрытия ин- формации. Если вы всегда будете использовать
Coordinate вместо float или double,
вы эффективно спрячете исходный тип данных. В C++ это практически все воз- можное сокрытие информации, которое язык позволяет сделать разработчику. Все последующие пользователи вашего кода должны соблюдать дисциплину и не смот- реть на определение
Coordinate. C++ предоставляет скорее фигуральную, а не бук- вальную возможность сокрытия информации.
Другие языки, например Ada, делают шаг вперед и поддерживают буквальное со- крытие информации. Вот как фрагмент кода для типа
Coordinate будет выглядеть в модуле Ada, где он был объявлен:
Пример сокрытия деталей реализации типа внутри модуля (Ada)
package Transformation is
Это выражение объявляет Coordinate скрытым в данном модуле.
type Coordinate is private;
Вот как тип
Coordinate будет выглядеть в другом модуле, где он используется:
Пример использования типа из другого модуля (Ada)
with Transformation;
procedure Routine1(...) ...
latitude: Coordinate;
longitude: Coordinate;
begin
-- операторы, использующие широту и долготу end Routine1;
Заметьте: тип
Coordinate объявлен в модуле как private. Это значит, что единственная часть программы, которая знает определение типа
Coordinate, — это закрытая часть модуля
Transformation. При групповой разработке проекта вы можете распрост- ранить только спецификацию модуля, что затруднит программисту, работающе- му с другим модулем, просмотр исходного типа
Coordinate. Информация будет
>
>
>