Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 874
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
306
ЧАСТЬ III Переменные буквально спрятана. Такие языки, как C++, которые требуют распространять определение типа
Coordinate в заголовочном файле, подрывают идею реального сокрытия информации.
Следующие примеры иллюстрируют несколько причин для создания собственных типов.
쐽
Упростить модификацию кода Сделать новый тип легко, а это дает вам большую гибкость.
쐽
Избежать излишнего распространения информации Явная типизация распространяет сведения о типе данных по всей программе вместо их цент- рализации в одном месте. Это пример принципа сокрытия информации с це- лью достижения централизации, (см. раздел 6.2).
쐽
Увеличить надежность В Ada вы можете объявлять типы как type Age is range
0..99. После этого компилятор генерирует проверки времени выполнения, чтобы удостовериться, что значение любой переменной типа
Age всегда попадает в диапазон
0..99.
쐽
Замаскировать недостатки языка Если ваш язык не содержит необходи- мого предопределенного типа, вы можете создать его сами. Например, в C нет булева или логического типа. Этот недостаток легко исправить, создав тип:
typedef int Boolean;
Почему приведены примеры создания типов
на языках Pascal и Ada?
Языки Pascal и Ada сейчас подобны динозаврам, а языки, заменившие их, в основном гораздо практичнее. Однако в области определения простых типов мне кажется,
что C++, Java и Visual Basic представляют случай трех шагов вперед и одного шага назад. В Ada такое объявление, как:
currentTemperature: INTEGER range 0..212;
содержит важную семантическую информацию, которую объявление:
int temperature;
не содержит. Если посмотреть глубже, то определение:
type Temperature is range 0..212;
currentTemperature: Temperature;
позволяет компилятору удостовериться, что
currentTemperature присваивается только другим переменным типа
Temperature, и такая дополнительная прослойка безопасности требует минимального кодирования.
Естественно, программист может создать класс
Temperature, чтобы реализовать те же семантические правила, автоматически предоставляемые в Ada, но между со- зданием простого типа данных в одну строку и созданием класса дистанция ог- ромного размера. Зачастую программист будет использовать простой тип данных,
но не станет делать дополнительных усилий для создания класса.
ГЛАВА 12 Основные типы данных
307
Основные принципы создания собственных типов
Имейте в виду следующие принципы, когда решите создавать собственный тип.
Создавайте типы с именами, отражающими их функ-
циональность Избегайте имен типов, которые ссылаются на данные, лежащие в основе этих типов. Используйте имена,
которые отражают те элементы реальной задачи, которые этот тип представляет. Определения из предыдущих приме- ров — понятно названные типы для координат и имен ра- ботников — это реальные сущности. Точно так же вы можете создавать типы для валюты, кодов платежей, возрастов и т. д., а именно для аспектов действительно существующих задач.
Будьте осторожны, создавая имена типов, ссылающиеся на предопределенные типы.
Такие имена, как
BigInteger или LongString, описывают компьютерные данные, а не конкретную задачу. Большое преимущество создания собственных типов данных состоит в том, что добавляется слой, изолирующий программу от языка разработки.
Имена типов, ссылающиеся на типы языка, лежащие в их основе, нарушают эту изоляцию. Они не дают вам большого преимущества по сравнению с примене- нием предопределенных типов. Проблемно-ориентированные имена, с другой стороны, облегчают процесс внесения изменений и предоставляют самодокумен- тируемые объявления типов.
Избегайте предопределенных типов Если есть хоть малейшая возможность,
что тип может измениться, избегайте применения предопределенных типов вез- де, кроме определений
typedef или type. Легко создать новые функционально-ори- ентированные типы — менять же данные в программе, использующей жестко за- кодированные типы, гораздо сложней. Более того, функционально-ориентирован- ные типы частично документируют объявленные с ними переменные. Объявле- ние
Coordinate x сообщит вам об x гораздо больше, чем объявление float x. Исполь- зуйте собственные типы везде, где только можно.
Не переопределяйте предопределенные типы Изменение определения стан- дартного типа может вызвать путаницу. Например, если в вашем языке есть пре- допределенный тип
Integer, не создавайте свой тип с именем Integer. Читающие ваш код могут забыть, что вы его переопределили, и будут считать, что видят тот же
Integer, который привыкли видеть.
Определите подстановки для переносимости В отличие от совета не изме- нять определение стандартных типов вы можете создать для этих типов подста- новки, так что на разных платформах переменные будут представлены одними и теми же сущностями. Так, вы можете определить тип
INT32 и использовать его вместо
int или тип LONG64 вместо long. Изначально единственной разницей между двумя типами будет применение заглавных букв. Но при переходе на другую плат- форму вы сможете переопределить варианты с большими буквами так, чтобы они совпадали с типами для данных аппаратных средств.
Не создавайте типы, которые легко перепутать с предопределенными. Существу- ет возможность определить
INT вместо INT32, но лучше сделать явное различие между типами, созданными вами, и типами, предоставленными языком програм- мирования.
Перекрестная ссылка В каждом случае следует решать, не луч- ше ли использовать класс, а не простой тип данных. Подробнее см. главу 6.
308
ЧАСТЬ III Переменные
Рассмотрите вопрос создания класса вместо использования typedef Прос- тые операторы
typedef позволяют проделать большой путь в сторону сокрытия ин- формации об исходном типе переменной. Однако иногда вам может потребоваться дополнительная гибкость и управляемость, которой позволяют добиться классы.
Подробнее см. главу 6.
Контрольный список: основные данные
Числа в общем
Не содержит ли код магические числа?
Предупреждаются ли в коде ошибки деления на ноль?
Очевидны ли преобразования типов?
Если переменные двух разных типов используются в од- ном выражении, будет ли оно вычислено так, как вы это предполагаете?
Не происходит ли сравнение переменных разных типов?
Компилируется ли программа без предупреждений ком- пилятора?
Целые числа
Работают ли выражения, содержащие целочисленное деление так, как это предполагалось?
Предупреждаются ли в целочисленных выражениях проблемы целочислен- ного переполнения?
Числа с плавающей запятой
Не содержит ли код операции сложения и вычитания слишком разных по величине чисел?
Предупреждаются ли в коде ошибки округления?
Не выполняется сравнение на равенство чисел с плавающей запятой?
Символы и строки
Не содержит ли код магических символов и строк?
Свободны ли операции со строками от ошибки потери единицы?
Различаются ли в коде на C строковые указатели и массивы символов?
Соблюдается ли в коде на C соглашение об объявлении строк с длиной
CONSTANT+1?
Используются ли в C массивы символов вместо указателей там, где это допустимо?
Инициализируются ли в C строки с помощью NULL во избежание бесконеч- ных строк?
Используются ли в коде на C strncpy() вместо strcpy()? А strncat() и strncmp()?
Логические переменные
Используются ли в программе дополнительные логические переменные для документирования проверок условия?
Используются ли в программе дополнительные логические переменные для упрощения проверок условия?
http://cc2e.com/1206
Перекрестная ссылка Список вопросов, затрагивающих дан- ные вообще, без подразделения на конкретные типы, см. в кон- трольном списке главы 10. Спи- сок вопросов по вариантам именования см. в контрольном списке главы 11.
ГЛАВА 12 Основные типы данных
309
Перечислимые типы
Используются ли в программе перечислимые типы вместо именованных констант ради их улучшенной читабельности, надежности и модифицируе- мости?
Используются ли перечислимые типы вместо логических переменных, если все значения переменной не могут быть переданы с помощью true и false?
Проверяются ли некорректные значения перечислимых типов в условных операторах?
Зарезервирован ли первый элемент перечислимого типа как недопустимый?
Перечислимые константы
Используются ли в программе именованные константы вместо магических чисел для объявления данных и границ циклов?
Используются ли именованные константы последовательно, чтобы одно значение не представлялось в одном месте константой, а в другом — лите- ралом?
Массивы
Находятся ли все индексы массива в его границах?
Свободны ли ссылки на массив от ошибок потери единицы?
Указаны ли все индексы многомерных массивов в правильном порядке?
В правильном ли порядке используются переменные-индексы во вложенных циклах, не происходит ли пересечения индексов?
Создание типов
Используются ли в программе отдельные типы для каждого вида данных,
который может измениться?
Ориентируются ли имена типов на реальные сущности, которые эти типы представляют, а не на типы языка программирования?
Достаточно ли наглядны имена типов, чтобы помочь документированию объявлений данных?
Не произошло ли переопределение предопределенных типов?
Рассматривался ли вопрос создания нового класса вместо простого пере- определения типа?
Ключевые моменты
쐽
Работа с определенными типами данных требует запоминания множества правил для каждого из них. Используйте список контрольных вопросов из этой главы, чтобы убедиться, что вы учли основные проблемы с ними.
쐽
Создание собственных типов, если ваш язык это позволяет, упрощает модифи- кацию вашей программы и делает ее более самодокументируемой.
쐽
Прежде чем создавать простой тип с помощью
typedef или его эквивалента,
подумайте, не следует ли создать вместо него новый класс.
310
ЧАСТЬ III Переменные
Г Л А В А 1 3
Нестандартные типы данных
Содержание
쐽
13.1. Структуры
쐽
13.2. Указатели
쐽
13.3. Глобальные данные
Связанные темы
쐽
Фундаментальные типы данных: глава 12
쐽
Защитное программирование: глава 8
쐽
Нестандартные управляющие структуры: глава 17
쐽
Сложность в разработке ПО: раздел 5.2
Некоторые языки программирования поддерживают экзотические виды данных в дополнение к типам, обсуждавшимся в главе 12. В разделе 13.1 рассказывается,
при каких обстоятельствах вы могли бы использовать структуры вместо классов.
В разделе 13.2 описываются детали использования указателей. Если у вас возни- кали проблемы с использованием глобальных данных, из раздела 13.3 вы узнае- те, как их избежать. Если вы думаете, что типы данных, описанные в этой главе,
— это не те типы, о которых вы обычно читаете в современных книгах по объек- тно-ориентированному программированию, то вы абсолютно правы. Поэтому эта глава и называется «
Нестандартные типы данных».
13.1. Структуры
Термин «структура» относится к типу данных, построенному на основе других типов. Так как массивы — особый случай, они рассматриваются отдельно в главе
12. В этом разделе обсуждаются структурированные данные, созданные пользо- вателем:
structs в C/C++ и Structures в Microsoft Visual Basic. В Java и C++ классы тоже иногда выглядят, как структуры (когда они состоят только из открытых членов данных и не содержат открытые методы).
Чаще всего вы предпочтете создавать классы, а не структуры, чтобы задейство- вать преимущества закрытости и функциональности, предлагаемой классами, в http://cc2e.com/1378
1 ... 35 36 37 38 39 40 41 42 ... 104
ГЛАВА 13 Нестандартные типы данных
311
дополнение к открытым данным, поддерживаемым структурами. Но иногда пря- мое манипулирование блоками данных может быть полезно.
Используйте структуры для прояснения взаимоотношений между дан-
ными Структуры объединяют группы взаимосвязанных элементов. Иногда труднее всего понять, какие данные в программе используются вместе. Это похоже на прогулку по маленькому городку с вопросами о том, кто с кем в родстве. Вы вы- ясняете, что каждый кому-то кем-то приходится, но вы не уверены в этом, и ни- когда не можете получить внятного ответа.
Если данные хорошо структурированы, выяснение, что с чем связано, сильно упрощается. Вот пример данных, которые не были структурированы:
Пример неструктурированных, вводящих в заблуждение переменных (Visual Basic)
name = inputName address = inputAddress phone = inputPhone title = inputTitle department = inputDepartment bonus = inputBonus
Так как данные не структурированы, кажется, что эти операторы присваивания объединены. На самом деле, переменные
name, address, и phone относятся к ря- довому служащему, а
title, department и bonus — к менеджеру. В этом фрагменте нет подсказок о том, что используется два вида данных. В следующем фрагменте применение структур делает взаимоотношения яснее:
Пример более информативных, структурированных переменных (Visual Basic)
employee.name = inputName employee.address = inputAddress employee.phone = inputPhone supervisor.title = inputTitle supervisor.department = inputDepartment supervisor.bonus = inputBonus
В этом коде, содержащем структурированные переменные, очевидно, что часть данных относится к работнику, а остальные — к менеджеру.
Используйте структуры для упрощения операций с блоками данных Вы можете объединить взаимосвязанные элементы в структуру и выполнять опера- ции над ней. Проще обрабатывать структуру целиком, чем выполнять те же дей- ствия над каждым элементом. Это надежней и требует меньше строк кода.
Допустим, у вас есть группа взаимосвязанных элементов данных — скажем, ин- формация о работнике в базе данных персонала. Если данные не объединены в структуру, простое копирование всей группы может потребовать большого чис- ла операторов. Вот пример на Visual Basic:
312
ЧАСТЬ III Переменные
Пример громоздкого копирования группы (Visual Basic)
newName = oldName newAddress = oldAddress newPhone = oldPhone newSsn = oldSsn newGender = oldGender newSalary = oldSalary
Каждый раз, желая передать сведения о работнике, вам приходится иметь дело со всеми этими операторами. Если вам нужно добавить новый элемент данных, на- пример
numWithholdings , вам придется найти все места, где написаны эти при- сваивания, и добавить еще одно:
newNumWithholdings = oldNumWithholdings.
Представьте, как ужасно будет менять местами данные о двух работниках. Вам не надо напрягать воображение — вот пример:
Пример утомительного способа
обмена двух групп данных (Visual Basic)
‘ Поменять местами старые и новые данные о работнике.
previousOldName = oldName previousOldAddress = oldAddress previousOldPhone = oldPhone previousOldSsn = oldSsn previousOldGender = oldGender previousOldSalary = oldSalary oldName = newName oldAddress = newAddress oldPhone = newPhone oldSsn = newSsn oldGender = newGender oldSalary = newSalary newName = previousOldName newAddress = previousOldAddress newPhone = previousOldPhone newSsn = previousOldSsn newGender = previousOldGender newSalary = previousOldSalary
Более легкое решение проблемы — объявить структурную переменную:
Пример объявления структуры (Visual Basic)
Structure Employee name As String address As String phone As String ssn As String gender As String salary As long