Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4089 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 8:

Чувство стиля

Выбор правильных имен

Первое, что нуждается в регулировании, - это выбор имен. Имена компонентов следует строго контролировать, что всем принесет пользу.

Общие правила

Наиболее значимыми являются имена классов и компонентов, широко используемые в других классах.

Для этих имен используйте полные слова, но не аббревиатуры, если только последние не имеют широкого применения в проблемной области. В классе PART, описывающем детали в системе управления складом, назовите number, а не num, компонент (запрос), возвращающий номер детали. Печатание недорого стоит, сопровождение - чрезвычайно дорого. Аббревиатуры usa в Географической Информационной системе или copter в системе управления полетами вполне приемлемы, так как в данных областях приобрели статус независимых слов. Кроме того, некоторые сокращения используются годами и также приобрели независимый статус, такие как PART для PARTIAL, например в имени класса PART_COMPARABLE, описывающего объекты, поставляемые с частичным порядком.

При выборе имен целью является ясность. Без колебаний используйте несколько слов, объединенных пробелами, как в имени класса ANNUAL_RATE, или yearly_premium в имени компонента.

Хотя современные языки не ограничивают длину идентификаторов, и рассматривают все буквы как важные, длина имени должна оставаться разумной. Правила на этот счет для классов и компонентов различные. Имена классов вводятся только в ряде случаев - в заголовках класса, объявлениях типа, предложениях наследования и других. Имя класса должно полностью характеризовать соответствующую абстракцию данных, так что вполне допустимо такое имя класса - PRODUCT_QUANTITY_INDEX_ EVALUATOR. Для компонентов достаточно двух слов, редко трех, соединенных подчеркиванием. В частности, не следует допускать излишней квалификации имени компонента. Если имя компонента чересчур длинно, то это, как правило, из-за излишней квалификации.

Правило: Составные имена компонентов

Не включайте в имя компонента имя базовой абстракции данных (служащей именем класса).

Компонент, задающий номер части в PART, должен называться просто number, а не part_number. Подобная сверхквалификация является типичной ошибкой новичков, скорее затуманивающей, чем проясняющей текст. Помните, каждое использование компонента однозначно определяет класс, например part1.number, где part1 должно быть объявлено типа PART или его потомка.

Для составных имен лучше избегать стиля, популяризируемого Smalltalk и используемого в библиотеках, таких как X Window System, объединяющих несколько слов вместе, начиная каждое внутренне слово с большой буквы, как в yearlyPremium. Вместо этого разделяйте компоненты подчеркиванием, как в yearly_premium. Использование внутренних больших букв конфликтует с соглашениями обычного языка и выглядит безобразно, оно приводит к трудно распознаваемому виду, следовательно, к ошибкам (сравните aLongAndRatherUnreadableIdentifier и an_even_longer_but_perfectly_clear_choice_of_name ).

Иногда каждый экземпляр некоторого класса содержит поле, представляющее экземпляр другого класса. Это приводит к мысли использовать для имени атрибута имя класса. Например, вы определили класс RATE, а классу ACCOUNT потребовался один атрибут типа RATE, для которого кажется естественным использовать имя rate - в нижнем регистре, в соответствии с правилами, устанавливаемыми ниже. Хотя можно пытаться найти более специфическое имя, но приемлемо rate: RATE. Правила выбора идентификаторов допускают одинаковые имена компонента и класса. Нарушением стиля является добавление префикса the, как в the_rate, что только добавляет шумовую помеху.

Локальные сущности и аргументы подпрограмм

Акцент на ясные, хорошо произносимые имена сделан для компонентов и классов. Для локальных сущностей и аргументов подпрограмм, имеющих локальную область действия, нет необходимости в подобной выразительности. Имена, несущие слишком много смысла, могут даже ухудшить читабельность текста, придавая слишком большое значение вспомогательным элементам. (Им можно давать короткие однобуквенные имена, как, например, в процедуре класса TWO_WAY_LIST из библиотеки e Base )

move (i: INTEGER) is
        -- Поместить курсор в позицию i или after, если i слишком велико
    local
        c: CURSOR; counter: INTEGER; p: like FIRST_ELEMENT
    ...
remove is
        -- Удаляет текущий элемент; перемещает cursor к правому соседу
        -- (или after если он отсутствует).
    local
        succ, pred, removed: like first_element
    ...

Если бы succ и pred были бы компонентами, они бы назывались successor и predecessor. Принято использовать имя new для локальной сущности, представляющей новый объект, создаваемый программой, и имя other для аргумента, представляющего объект того же типа, что и текущий, как в объявлении для clone в GENERAL:

frozen clone (other: GENERAL): like other is...

Регистр

Регистр не важен в нашей нотации, поскольку было бы опасным позволять двум почти идентичным идентификаторам обозначать разные вещи. Но настоятельно рекомендуется в интересах читабельности и согласованности придерживаться следующих правил:

  • Имена классов задаются буквами в верхнем регистре: POINT, LINKED_LIST, PRICING_MODEL. Это верно и для формальных родовых параметров, обычно начинающихся с G.
  • Имена неконстантных атрибутов, подпрограмм, отличных от однократных, локальных сущностей и аргументов подпрограмм задаются полностью в нижнем регистре: balance, deposit, succ, i.
  • Константные атрибуты начинаются с буквы в верхнем регистре, за которой следуют буквы нижнего регистра: Pi: INTEGER is 3.141598524 ; Welcome_message: STRING is "Welcome!". Это же правило применяется к уникальным значениям, представляющих константные целые.
  • Те же соглашения применяются к однократным функциям, эквиваленту констант для небазисных типов: Error_window, Io. В нашем первом примере комплексное число i (мнимая единица) осталось в нижнем регистре для совместимости с математическими соглашениями.

Эти правила касаются имен, выбираемых разработчиком. Резервируемые слова нотации разделяются на две категории. Ключевые слова, такие как do и class, играют важную синтаксическую роль; они записаны в нижнем регистре полужирным шрифтом. Несколько зарезервированных слов не являются ключевыми, поскольку играют ассоциированную семантическую роль, они записываются курсивом с начальной буквой в верхнем регистре, подобно константам. К ним относятся: Current, Result, Precursor, True и False.

Грамматические категории

Точные правила управляют грамматическими категориями слов, используемых в идентификаторах. В некоторых языках эти правила могут применяться без колебаний, в английском, как ранее отмечалось, они обеспечивают большую гибкость.

Правило для имен классов уже приводилось: следует всегда использовать существительные, как в ACCOUNT, возможно квалифицированные, как в LONG_TERM_SAVINGS_ACCOUNT, за исключением случая отложенных классов, описывающих структурные свойства, для которых могут использоваться прилагательные, как в NUMERIC или REDEEMABLE.

Имена подпрограмм должны отражать принцип Разделения Команд и Запросов:

  • Процедуры (команды) должны быть глаголами в инфинитиве или повелительной форме, возможно, с дополнениями: make, move, deposit, set_color.
  • Атрибуты и функции (запросы) никогда не должны использовать императив или инфинитив глаголов: никогда не называйте запрос get_value, назовите его просто value. Имена небулевых запросов должны быть существительными, такими как number, возможно, квалифицированными, как в last_month_balance. Булевы запросы должны использовать прилагательные, как в full. В английском возможна путаница между прилагательными и глаголами ( empty, например, может значить "пусто ли это?" или "опустошить это!"). В связи с этим для булевых запросов часто применяется is_ форма, как в is_ empty.

Стандартные имена

Вы уже заметили многократное использование во всей книге нескольких базисных имен, таких как put и item. Они являются важной частью метода.

Большинству классов необходимы компоненты, представляющие операции нескольких базисных видов: вставка, замена, доступ к элементам структуры. Вместо придумывания специальных имен для этих и подобных операций в каждом классе, предпочтительно повсюду применять стандартную терминологию.

Вот каковы основные стандартные имена. Начнем с процедур создания: имя make рекомендуется для наиболее общей процедуры создания и имена вида make_some_qualification, например, make_polar и make_cartesian для классов POINT или COMPLEX.

Для команд наиболее общие имена приведены в таблице:

Таблица 8.1. Стандартные имена команд
Команда Действие
extend Добавить элемент
replace Заменить элемент
force Подобна команде put, но может работать для большего числа случаев. Например для массивов put имеет предусловие, требующее, чтобы индекс не выходил за границы, в то время как force не имеет предусловий и допускает выход за границы
remove Удаляет (не специфицированный) элемент
prune Удаляет специфицированный элемент
wipe_out Удаляет все элементы

Для небулевых запросов (атрибутов или функций)

Таблица 8.2. Стандартные имена для не булевых запросов
Запрос Действие
item Базисный запрос для получения элемента: в классе ARRAY - элемент с заданным индексом; STACK - элемент вершины стека; QUEUE - "старейший" элемент и так далее
infix "@" Синоним item в некоторых случаях, например в классе ARRAY
count Число используемых элементов структуры
capacity Физический размер, распределенный для ограниченной структуры, измеряемый числом потенциальных элементов. Инвариант должен включать 0<count and count <= capacity

Для булевых запросов стандартными именами являются:

Таблица 8.3. Стандартные имена булевых запросов
Запрос Действие
empty Содержит ли структура элементы?
full Заполнена ли структура ограниченной емкости элементами? Обычно эквивалент count = capacity
has Присутствует ли заданный элемент в структуре? (Базисный тест проверки членства)
extendible Можно ли добавить элемент? (Может служить предусловием для extend )
prunable Можно ли удалить элемент? (Может служить предусловием для remove и prune )
readable Доступен ли элемент? (Может служить предусловием для remove и item )
writable Можно ли изменить элемент? (Может служить предусловием для extend, replace, put и др.)

Преимущества согласованного именования

Схема имен, данная выше, вносит наиболее видимый вклад в характерный стиль конструирования ПО, разрабатываемый в соответствии с принципами этой книги.

Не зашли ли мы слишком далеко в борьбе за согласованность? Не получится ли, что в программах будут использоваться одни и те же имена, но с разной семантикой? Например, item для стека возвращает элемент вершины, а для массива - элемент с заданным индексом.

При систематическом подходе к ОО-конструированию, использованию статической типизации и Проектированию по Контракту этот страх не оправдан. Знакомясь с компонентом, автор клиента может полагаться на четыре вида свойств, представленных в краткой форме класса:

  • F1 Его имя.
  • F2 Его сигнатура (число и типы аргументов, если это процедура, тип результата для запросов).
  • F3 Предусловие и постусловие, если они заданы.
  • F4 Заголовочный комментарий.

Подпрограммы имеют, конечно же, тело, но предполагается, что тело не должно волновать клиента.

Три из этих элементов будут отличаться для вариантов базисных операций. Например, в краткой форме класса STACK можно найти компонент:

put (x: G)
        -- Втолкнуть x на вершину
    require
        writable: not full
    ensure
        not_empty: not empty
        pushed: item = x

В классе ARRAY появляется однофамилец:

put (x: G; i: INTEGER)
        -- Заменить на x значение элемента с индексом i
    require
        not_too_small: i >= lower
        not_too_large: i <= upper
    ensure
        replaced: item (i) = x

Сигнатуры различаются, предусловия, постусловия, заголовочные комментарии - все различно. Использование имени put, не создавая путаницы, обращает внимание читателя на общую роль этих процедур: они обе обеспечивают базисный механизм изменений.

Эта согласованность оказывается одним из наиболее привлекательных аспектов метода, в частности библиотек. Новые пользователи быстро привыкают к ней и при появлении нового класса, следующего стандартному стилю, принимают его как старого знакомого и могут сразу же сосредоточиться на нужных им компонентах.