Главная страница
Навигация по странице:

  • Пример использования макроса ARRAY_LENGTH() для операций с массивами на языке C

  • 12.9. Создание собственных типов данных (псевдонимы)

  • Пример создания типа (C++)

  • Перекрестная ссылка

  • Пример измененного определения типа (C++)

  • Пример создания типа для имен работников (Pascal)

  • Пример лучшего создания типа (Pascal) Const> ГЛАВА 12

  • Пример сокрытия деталей реализации типа внутри модуля (Ada)

  • Пример использования типа из другого модуля (Ada)

  • Упростить модификацию кода

  • Замаскировать недостатки языка

  • Почему приведены примеры создания типов на языках Pascal и Ada

  • ГЛАВА 12

  • Создавайте типы с именами, отражающими их функ

  • Избегайте предопределенных типов

  • Не переопределяйте предопределенные типы

  • Определите подстановки для переносимости

  • Рассмотрите вопрос создания класса вместо использования

  • Контрольный список: основные данные Числа в общем

  • Числа с плавающей запятой

  • ЧАСТЬ III

  • Руководство по стилю программирования и конструированию по


    Скачать 7.6 Mb.
    НазваниеРуководство по стилю программирования и конструированию по
    Дата18.05.2023
    Размер7.6 Mb.
    Формат файлаpdf
    Имя файлаCode_Complete.pdf
    ТипРуководство
    #1139697
    страница39 из 104
    1   ...   35   36   37   38   39   40   41   42   ...   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. Информация будет
    >
    >
    >

    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


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