Хотя правила, представленные здесь, не столь фундаментальны, как принципы ОО-конструирования ПО, было бы глупо рассматривать их просто как "косметику". Хорошее ПО хорошо в большом и в малом - в архитектуре высокого уровня и в деталях низкого уровня. Качество деталей еще не гарантирует качества в целом, но небрежность в деталях верный признак более серьезных ошибок. (Если проект не выглядит красивым, то заказчики не поверят, что вы справились с по-настоящему трудным делом.) Серьезный инженерный процесс требует все делать правильно: великолепно и современно.
Так что не следует пренебрегать, казалось бы, такими пустяками как форматирование текста и выбор имен. Может показаться удивительным перейти, не снижая уровня внимания, от математических понятий достаточной полноты
Некоторые подсказки можно получить, исходя из понятия стиля в его литературном смысле. Хотя, говоря о хорошем произведении, на первом месте стоит способность автора создать соответствующую структуру и сюжет, никакой текст не будет успешным, пока в нем не все отработано: каждый абзац, каждое предложение, каждое слово.
Можно проверять, выполняются ли правила стиля. Лучше, если они навязаны инструментарием и выполняются изначально. Однако инструментарий позволяет далеко не все, и нет замены тщательности в написании каждого участка ПО.
Часто программист откладывает применение правил стиля. Он пишет программный код как придется, полагая: "я почищу все позже, сейчас я даже не знаю, что из написанного мне пригодится". Это не наш путь. Если правило используется, не должно быть никакой задержки в его применении с первых шагов написания ПО, даже в отсутствие специальных средств поддержки. Всегда дороже последующие корректировки текста, чем его корректное написание с самого начала. Всегда есть риск, что на чистку не хватит времени или вы просто забудете о ней. Всякий, кто позже столкнется с вашей работой, потратит куда больше времени, чем это стоило бы вам при написании заголовочных комментариев, придумывании подходящих имен, применении нужного форматирования. Не забывайте: этим кто-то можете быть вы сами.
Стиль ПО всегда колебался между краткостью и многословием. Двумя крайними примерами языков программирования являются, вероятно,
Существенными для нас являются ясность и качество. Обе экстремальные формы противоречат этим целям. Зашифрованные C-программы, к несчастью, не ограничены известной дискуссией об "
Стиль правил этой лекций представляет смесь ясности, характерной для disp (двусмысленно), а не display (ясно и четко), или класс ACCNT (непроизносимое) вместо ACCOUNT. В данных ситуациях нет налога на число нажатий. Но в то же время, когда приходится исключать напрасную избыточность, правила безжалостны. Они ограничивают заголовки комментариев обязательными словами, освобождают от всех "the" и других подобных любезностей; они запрещают излишнюю квалификацию (подобную account_balance в классе ACCOUNT, где имени balance достаточно). Возвращаясь к доминантной теме, правила допускают группирование связанных составляющих сложной структуры в одной сроке, например:
from i := 1 invariant i <= n until i = n loop.
Этой комбинации ясности и краткости следует добиваться в своих текстах. Раздутый размер текста, в конечном счете, приводят к возрастанию сложности, но и не экономьте на размере, когда это необходимо для обеспечения ясности.
Если, подобно многим, вас интересует, будет ли текст ОО-реализации меньше, чем текст на языках C, Pascal,
Если вас заботят размеры кода, убедитесь, что вы позаботились об архитектурных аспектах. Следует быть краткими при выражении сути алгоритма, но не экономьте на нажатиях клавиш ценой ясности.
Большинство правил дает однозначное толкование без всяких вариантов. Исключения включают использование шрифтов, управляемое внешними обстоятельствами (что хорошо выглядит в книге, может быть не видимо на слайдах проектора), и точки с запятыми, для которых существуют две противоположные школы с весомыми аргументами.
Правила появились в результате многолетних наблюдений, дискуссий и тщательного анализа того, что работает и что работает менее хорошо. Даже при этом часть правил может показаться спорной и некоторые решения являются делом вкуса, так что разумные люди по ряду соображений могут с ними не согласиться. Если вы не принимаете какое-либо из рекомендуемых соглашений, вам следует определить свое собственное, пояснить его во всех деталях, явно документировать. Но тщательно все взвесьте, прежде чем принимать такое решение, - так очевидны преимущества
Многие из этих правил стиля первоначально разрабатывались для библиотек, а затем нашли свое место в разработке обычного ПО. В объектной технологии, конечно, все ПО разрабатывается в предположении, что, если оно и не предназначается для повторного использования, со временем оно может стать таковым, поэтому естественно с самого начала применять те же правила стиля.
Подобно правилам проектирования, правила стиля применяются в примерах этой книги. Причины очевидны: каждый должен практиковать то, что он проповедует, не говоря о том, что правила способствуют ясности мысли и выражения при представлении ОО-метода.
Единственными исключениями являются некоторые отходы в форматировании программных текстов. Согласно правилам, программные тексты без колебаний следует располагать на многих строчках, требуя лишь, например, чтобы каждое предложение утверждения имело свою собственную метку. Строки компьютерного экрана не являются ресурсом, который нуждается в экономии. Полагается, что со временем будет произведена революция, и мы перейдем от стиля папирусных свитков к странично-структурированным книгам. Но данный текст определенно является книгой - постоянное применение правил форматирования программных текстов привело бы к неоправданному увеличению объема книги.
Эти случаи освобождения от обязательств распространяются лишь на несколько правил форматирования и будут специально отмечены ниже при представлении правил. Такие исключения разрешаются только для представлений на бумаге. Фактические программные тексты применяют правила буквально.
Было бы ошибкой протестовать против правил этой лекции на том основании, что они ограничивают творческую активность разработчиков. Согласованный стиль скорее помогает, чем препятствует творчеству, направляя его в нужное русло. Большая часть усилий при производстве ПО тратится на чтение уже существующих текстов. Индивидуальные предпочтения в стиле дают преимущества одному человеку, общие соглашения - помогают каждому.
В программистской литературе семидесятых годов пропагандировалась идея " безликого программирования " ("egoless programming"): разрабатывать ПО так, чтобы личность автора в нем не ощущалась. Ее цель - возможность взаимозаменяемости разработчиков. Примененная к проектированию системы, цель эта становится явно нежелательной, даже если некоторые менеджеры страстно желают этого. Приведу отрывок из книги Барри Боема, цитирующей эту идею: " Программистский творческий инстинкт должен быть полностью затушеван в интересах общности и понятности ". Сам Боем комментирует это так: "Давать программистам подобные советы, зная их повышенную мотивацию, - заведомо обрекать их на нервное расстройство".
Какого качества можно ожидать от ПО с безликим проектом и безликим выражением?
Более чем удивительно, но при разработке ПО почти полностью отсутствуют стандарты стиля. Нет другой дисциплины, которая называлась бы "инженерией", где был бы такой простор для персональных прихотей и капризов. Чтобы стать профессионалами, разработчики ПО должны контролировать сами себя и выработать свои стандарты.
Первое, что нуждается в регулировании, - это выбор имен. Имена компонентов следует строго контролировать, что всем принесет пользу.
Наиболее значимыми являются имена классов и компонентов, широко используемые в других классах.
Для этих имен используйте полные слова, но не аббревиатуры, если только последние не имеют широкого применения в проблемной области. В классе PART, описывающем детали в системе управления складом, назовите number, а не num, компонент (запрос), возвращающий номер детали. Печатание недорого стоит, сопровождение - чрезвычайно дорого. Аббревиатуры usa в Географической Информационной системе или copter в системе управления полетами вполне приемлемы, так как в данных областях приобрели статус независимых слов. Кроме того, некоторые сокращения используются годами и также приобрели независимый статус, такие как PART для PARTIAL, например в имени класса PART_COMPARABLE, описывающего объекты, поставляемые с частичным порядком.
При выборе имен целью является ясность. Без колебаний используйте несколько слов, объединенных пробелами, как в имени класса ANNUAL_RATE, или yearly_premium в имени компонента.
Хотя современные языки не ограничивают длину идентификаторов, и рассматривают все буквы как важные, длина имени должна оставаться разумной. Правила на этот счет для классов и компонентов различные. Имена классов вводятся только в ряде случаев - в заголовках класса, объявлениях типа, предложениях наследования и других. Имя класса должно полностью характеризовать соответствующую абстракцию данных, так что вполне допустимо такое имя класса - PRODUCT_QUANTITY_INDEX_ . Для компонентов достаточно двух слов, редко трех, соединенных подчеркиванием. В частности, не следует допускать излишней квалификации имени компонента. Если имя компонента чересчур длинно, то это, как правило, из-за излишней квалификации.
Правило: Составные имена компонентов Не включайте в имя компонента имя базовой абстракции данных (служащей именем класса). |
Компонент, задающий номер части в PART, должен называться просто number, а не part_number. Подобная сверхквалификация является типичной ошибкой новичков, скорее затуманивающей, чем проясняющей текст. Помните, каждое использование компонента однозначно определяет класс, например part1.number, где part1 должно быть объявлено типа PART или его потомка.
Для составных имен лучше избегать стиля, популяризируемого 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 были бы компонентами, они бы назывались и . Принято использовать имя new для локальной сущности, представляющей новый объект, создаваемый программой, и имя other для аргумента, представляющего объект того же типа, что и текущий, как в объявлении для в 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. В нашем первом примере Эти правила касаются имен, выбираемых разработчиком. Резервируемые слова нотации разделяются на две категории. Ключевые слова, такие как do и class, играют важную синтаксическую роль; они записаны в нижнем регистре полужирным шрифтом. Несколько зарезервированных слов не являются ключевыми, поскольку играют ассоциированную семантическую роль, они записываются курсивом с начальной буквой в верхнем регистре, подобно константам. К ним относятся: Current, Result, Precursor, True и False.
Точные правила управляют грамматическими категориями слов, используемых в идентификаторах. В некоторых языках эти правила могут применяться без колебаний, в английском, как ранее отмечалось, они обеспечивают большую гибкость.
Правило для имен классов уже приводилось: следует всегда использовать существительные, как в ACCOUNT, возможно квалифицированные, как в LONG_TERM_SAVINGS_ACCOUNT, за исключением случая отложенных классов, описывающих структурные свойства, для которых могут использоваться прилагательные, как в или 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 или .
Для команд наиболее общие имена приведены в таблице:
| Команда | Действие |
|---|---|
extend |
Добавить элемент |
replace |
Заменить элемент |
force |
Подобна команде put, но может работать для большего числа случаев. Например для массивов put имеет предусловие, требующее, чтобы индекс не выходил за границы, в то время как force не имеет предусловий и допускает выход за границы |
remove |
Удаляет (не специфицированный) элемент |
|
Удаляет специфицированный элемент |
wipe_out |
Удаляет все элементы |
Для небулевых запросов (атрибутов или функций)
| Запрос | Действие |
|---|---|
item |
Базисный запрос для получения элемента: в классе ARRAY - элемент с заданным индексом; STACK - элемент QUEUE - "старейший" элемент и так далее |
|
Синоним item в некоторых случаях, например в классе ARRAY |
count |
Число используемых элементов структуры |
|
Физический размер, распределенный для ограниченной структуры, измеряемый числом потенциальных элементов. Инвариант должен включать 0<count and count <= |
Для булевых запросов стандартными именами являются:
| Запрос | Действие |
|---|---|
empty |
Содержит ли структура элементы? |
full |
Заполнена ли структура ограниченной емкости элементами? Обычно эквивалент count = |
has |
Присутствует ли заданный элемент в структуре? (Базисный тест проверки членства) |
extendible |
Можно ли добавить элемент? (Может служить предусловием для extend ) |
prunable |
Можно ли удалить элемент? (Может служить предусловием для remove и ) |
|
Доступен ли элемент? (Может служить предусловием для remove и item ) |
|
Можно ли изменить элемент? (Может служить предусловием для extend, replace, put и др.) |
Схема имен, данная выше, вносит наиболее видимый вклад в характерный стиль конструирования ПО, разрабатываемый в соответствии с принципами этой книги.
Не зашли ли мы слишком далеко в борьбе за согласованность? Не получится ли, что в программах будут использоваться одни и те же имена, но с разной семантикой? Например, item для стека возвращает элемент вершины, а для массива - элемент с заданным индексом.
При систематическом подходе к ОО-конструированию, использованию
Подпрограммы имеют, конечно же, тело, но предполагается, что тело не должно волновать клиента. |
Три из этих элементов будут отличаться для вариантов базисных операций. Например, в краткой форме класса 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, не создавая путаницы, обращает внимание читателя на общую роль этих процедур: они обе обеспечивают базисный механизм изменений.
Эта согласованность оказывается одним из наиболее привлекательных аспектов метода, в частности библиотек. Новые пользователи быстро привыкают к ней и при появлении нового класса, следующего стандартному стилю, принимают его как старого знакомого и могут сразу же сосредоточиться на нужных им компонентах.
Многие алгоритмы используют константы. Как отмечалось в одной из предыдущих лекций, у констант плохая слава из-за отвратительной практики изменения их значений. Следует предусмотреть меры против подобного непостоянства.
Основное правило использования констант утверждает, что не следует явно полагаться на значения:
Принцип Символических констант Не используйте манифестные ( |
Манифестная константа задается явно своим значением, как, например, 50 (целочисленная константа) или " Cannot find file " (строковая константа). Принцип запрещает использование инструкций в форме:
population_array.make (1, 50)
или
print ("Cannot find file") -- Ниже смотри смягчающий комментарий
Вместо этого следует объявить соответствующий константный атрибут и в телах подпрограмм, где требуются значения, обозначать их именами атрибутов:
US_state_count: INTEGER is 50 file_not_found: STRING is "Cannot find file" ... population_array.make (1, state_count) ... print (file_not_found)
Преимущества очевидны: если появится новый штат или изменится сообщение, достаточно изменить только одно объявление.
Использование 1 наряду со state_count в первой инструкции не является нарушением принципа, так как он запрещает манифестные константы, отличные от нулевых элементов. Нулевыми элементами, допустимыми в манифестной форме, являются целые 0 и 1 (нулевые элементы сложения и умножения), вещественное число 0.0, нулевой символ, записываемый как ' %0 ', пустая строка - "". Использование символической константы One каждый раз, когда требуется сослаться на нижнюю границу массива (1 используется соглашением умолчания), свидетельствовало бы о педантичности, фактически вело бы к ухудшению читабельности.
В других обстоятельствах 1 может просто представлять системный параметр, имеющий сегодня одно значение, а завтра другое. Тогда следует объявить символическую константу, как например |
Принцип
Если число локальных константных атрибутов в классе становится большим, то, вероятно, имеет место нераспознанная абстракция данных - определенное понятие, характеризуемое рядом параметров.
Тогда желательно сгруппировать объявления констант, поместив их в отдельный класс, который может служить предком для любого класса, которому нужны константы. (Некоторые разработчики предпочитают в таких случаях использовать отношение клиента.) Примером является класс ASCII библиотеки Base.
Хотя формальные элементы класса несут достаточно подробную информацию, следует сопровождать класс неформальными пояснениями. Заголовочные комментарии к подпрограммам и предложения feature, дополненные предложениями indexing, задаваемыми для каждого класса, отвечают этой потребности.
Подобно дорожным знакам на улицах Нью-Йорка, говорящим "Даже и не думайте здесь парковаться!", знаки на входе в отдел программистов должны предупреждать " Даже и не думайте написать подпрограмму без заголовочного комментария". Этот комментарий, следующий сразу за ключевым словом is, кратко формулирует цель программы; он сохраняется в краткой и плоской краткой форме класса:
distance_to_origin: REAL is
-- Расстояние до точки (0, 0)
local
origin: POINT
do
create origin
Result := distance (origin)
end
Обратите внимание на отступ: комментарий начинается на шаг правее тела подпрограммы.
Комментарий к заголовку должен быть информативным, кратким, ясным. Он имеет собственный стиль, которому будем обучаться на примере, начав вначале с несовершенного комментария, а затем улучшая его шаг за шагом. В классе CIRCLE к одному из запросов возможен такой комментарий:
tangent_from (p: POINT): LINE is
-- Возвращает касательную линию к текущей
-- окружности,
-- проходящую через данную точку p,
-- если эта точка лежит вне текущей окружности
require
outside_circle: not has (p)
...
В стиле этого комментария много ошибок. Во-первых, он не должен начинаться "Возвращает ..." или "Вычисляет ... ", используя глагольные формы, поскольку это противоречит принципу Разделения Команд и Запросов. Имя, возвращаемое не булевым запросом, типично использует квалифицированное существительное. Поэтому лучше написать так:
-- Касательная линия к текущей окружности,
-- проходящая через данную точку p,
-- если эта точка лежит вне текущей окружности
Так как комментарий теперь не предложение, а просто квалифицированное имя, то точку в конце ставить не надо. Теперь следует избавиться от дополнительных слов (в английском особенно от the ), не требуемых для понимания. Для комментариев желателен телеграфный стиль. (Помните, что читатели, любящие литературные красоты, могут выбрать для чтения романы Марселя Пруста.)
-- Касательная линия к текущей окружности,
-- проходящая через точку p,
-- если точка вне текущей окружности
Следующая ошибка содержится в последней строчке. Дело в том, что условие применимости подпрограммы - ее предусловие - not has (p), появится сразу после комментария в краткой форме, где оно выражено ясно и недвусмысленно. Поэтому нет необходимости в его перефразировке, что может привести только к путанице, а иногда и к ошибкам (типичная ситуация: предусловие в форме x >= 0 с комментарием "применимо только для положительных x ", а нужно "не отрицательных"); всегда есть риск при изменившемся предусловии забыть об изменении комментария. Наш пример теперь станет выглядеть так:
-- Касательная линия к текущей окружности из точки p
Еще одна ошибка состоит в использовании слов линия ( line ) и точка ( point ) при ссылках на результат и аргумент запроса: эта информация непосредственно следует из объявляемых типов LINE и POINT. Лучше использовать формальные объявления типов, которые появятся в краткой форме, чем сообщать эту информацию в неформальной форме комментария. Итак:
-- Касательная к текущей окружности из p
Наши ошибки состояли в излишнем дублировании информации - о типах, о требованиях предусловия. Из их анализа следует общее правило написания комментариев: исходите из того, что читатель компетентен в основах технологии, не включайте информацию, непосредственно доступную в краткой форме класса. Это, конечно, не означает, что никогда не следует указывать информацию о типах, например, в предыдущем примере Расстояние до точки (0,0) было бы двусмысленным без указания слова "точка" (point).
При необходимости сослаться на текущий экземпляр используйте фразы вида: текущая окружность, текущее число, вместо явной ссылки на сущность Current. Во многих случаях можно вообще избежать с упоминания текущего объекта, так как каждому программисту ясно, что компоненты при вызове применяются к текущему объекту. В данном примере наш заключительный комментарий выглядит так:
-- Касательная из p
На этом этапе осталось три слова, а начинали с трех строк из 18 длинных слов. Длина комментария сократилась примерно на 87%, мы можем считать, что упражнение на сокращение выполнено полностью, - сказать короче и яснее трудно.
Несколько общих замечаний. Отметим бесполезность в запросах фраз типа "Возвращает ...", других шумовых слов и фраз, которые следует избегать во всех подпрограммах: "Эта подпрограмма вычисляет (возвращает) ...", просто скажите, что делается. Вместо:
-- Эта программа записывает последний исходящий звонок
пишите
-- Записать исходящий звонок
Как показывает это пример, комментарий к командам (процедурам) должен быть в
has (v: G): BOOLEAN is
-- Появляется ли v в списке?
...
Соглашение управляет использованием программных сущностей - атрибутов, аргументов, появляющихся в комментариях. При наборе текста они выделяются курсивом (о других соглашениях на шрифт смотри ниже). В исходных программных текстах они всегда должны заключаться в кавычки, так что оригинальный текст выглядит так:
-- Появляется ли 'v' в списке?
Инструментарий, генерирующий краткую форму класса, использует это соглашение для обнаружения ссылок на сущности.
Нужно следить за согласованностью. Если функция класса имеет комментарий: "Длина строки", в другой процедуре не должна идти речь о "ширине" строки: "Изменить ширину строки", когда речь идет об одном и том же свойстве строки.
Все эти рекомендации применимы к подпрограммам. Поскольку экспортируемые атрибуты внешне ничем не должны отличаться от функций без аргументов, то они тоже имеют комментарий, появляющийся с тем же отступом, что и у функций:
count: INTEGER
-- Число студентов на курсе
Для закрытых атрибутов комментарии желательны, но требования к ним менее строгие.
Как вы помните, класс может иметь несколько предложений feature:
indexing
...
class LINKED_LIST [G] inherit ... creation
...
feature -- Initialization
make is ...
feature -- Access
item: G is ...
...
feature -- Status report
before: BOOLEAN is ...
...
feature -- Status setting
...
feature -- Element change
put_left (v: G) is ...
...
feature -- Removal
remove is ...
...
feature {NONE} -- Implementation
first_element: LINKABLE [G].
...
end
Одна из целей введения нескольких предложений feature состоит в том, чтобы придать разным компонентам различный статус экспорта. Но в данном примере все компоненты за исключением последнего доступны всем клиентам. Другой целью введения нескольких предложений feature, демонстрируемой данным примером, является группировка компонентов по категориям. Комментарий, находящийся на той же строке, что и ключевое слово feature, характеризует категорию. Такие комментарии, подобно заголовочным комментариям подпрограмм, обнаруживаются инструментарием, таким как short, создающим документацию и краткую форму класса.
Восемнадцать категорий с соответствующими комментариями стандартизованы в библиотеках Base, так что каждый компонент (из примерно 2000) принадлежит одной из них. В этом примере показаны некоторые из наиболее важных категорий. Status report соответствует опциям (устанавливаются компоненты в категории Status setting, не включенной в этот пример). Закрытые и выборочно экспортируемые компоненты появляются в категории Implementation. Эти стандартные категории появляются всегда в одном и том же порядке, известном инструментарию (через список, редактируемый пользователем), и будут сохраняться или переустанавливаться при выводе документации. Внутри каждой категории инструментарий перечисляет компоненты в алфавитном порядке для простоты поиска.
Категории покрывают широкий спектр областей применения, хотя для специальных проблемных областей могут понадобиться собственные категории.
Подобными заголовочным комментариям, но немного более формальными являются предложения индексирования, появляющиеся в начале каждого класса:
indexing
description: "Последовательные списки в цепном представлении"
names: "Sequence", "List"
contents: GENERIC
representation: chained
date: "$Date: 96/10/20 12:21:03 $"
revision: "$Revision: 2.4$"
...
class LINKED_LIST [G] inherit
...
Предложения индексирования строятся в соответствии с принципом Само-документирования аналогично встроенным утверждениям и заголовочным комментариям, позволяя включать в текст ПО возможную документацию. Для свойств, не появляющихся напрямую в программном тексте класса, можно включить индексирующие разделы в форме:
indexing_term: indexing_value, indexing_value, ...
где indexing_term является идентификатором, а каждое indexing_value является некоторым базисным элементом, таким как строка, целое и так далее. Идентификаторы разделов, имеющие альтернативные имена, позволяют потенциальным авторам клиентов отыскать нужный класс по именам ( names ), содержанию ( contents ), выбору представления ( ), информации об обновлениях ( ), информации об авторе и многому другому. В разделы включается все, что может облегчить понимание класса и поиск, использующий ключевые слова. Благодаря специальному инструментарию, поддерживающим повторное использование, облегчается задача разработчиков по поиску в библиотеках нужных им классов с нужными компонентами.
Как индексирующие термы, так и их значения могут быть произвольными, но возможности выбора фиксируются для каждого проекта. Множество стандартов, принятых в библиотеке Base, частично приведено в примере. Каждый класс должен иметь раздел description, значением которого index_value является строка, описывающая роль класса в терминах его экземпляров ( Последовательные списки..., но не "этот класс описывает последовательные списки", или "последовательный список", или "понятие последовательного списка" и т. д.). Для наиболее важных классов в этой книге - но не в коротких примерах, предназначенных для специальных целей - раздел description включался в предложение indexing.
Предыдущие правила применяются к стандартизованным комментариям, появляющимся в определенных местах и играющих специальную роль в документировании класса.
Во всех способах разработки ПО существует необходимость в комментариях отдельных участков выполняемых алгоритмов, поясняющих суть работы.
Есть еще одно использование комментариев, часто используемое на практике, но редко упоминаемое в учебниках. Я говорю здесь о технике преобразования некоторого участка кода в комментарий либо потому, что он не работает, либо он еще просто не готов. Эта практика, очевидно, требует замены специальными механизмами. Она уже обогатила язык новой глагольной формой - "закомментировать" (comment out). |
Каждый комментарий по уровню абстракции должен быть выше комментируемого примера. Известный контрпример: -- Увеличить i на 1 в инструкции i := i + 1. Здесь комментарий является перефразировкой кода и не несет полезной нагрузки.
Следующие правила определяют, как следует располагать программный текст на бумаге в реальной жизни или при моделировании этого процесса на дисплее компьютера. Более чем любые другие они взывают о "косметике" столь же важной для разработчиков ПО, как косметика от Кристиана Диора для ее покупательниц. Они играют немалую роль в том, как быстро ваш продукт будет понятен его пользователям - сопровождающим, повторно использующим, покупающим ваше ПО.
Рекомендуемое форматирование текста следует из общей синтаксической формы нотации, которую с некоторой натяжкой можно назвать "операторной do, например) или разделители (точка с запятой, запятая). Операндом является символ, выбираемый программистом (идентификатор, константа).
Основываясь на этом свойстве, форматирование текста следует гребенчато-подобной структуре (comb-like structure), введенной в языке
(рис 8.1) Гребенчато-подобная структура организации программного текстаКаждая ветвь гребенки является последовательностью чередующихся операторов и операндов, обычно начинающихся и заканчивающихся оператором. В пространстве между двумя ветвями находится либо операнд, либо рекурсивно гребенчато-подобная структура.
Как пример, зависящий от размера его составляющих a, b и c, допустимы следующие формы представления инструкции выбора:
if c then a else b end
или
if
c
then
a
else
b
end
или:
if c then
a
else b end
Однако вы не можете использовать строку, содержащую просто if c или c end, так как они включают операнд вместе с чем-то еще, пропуская заканчивающий оператор в первом случае, а во втором - начинающий.
Подобным образом можно начать класс после предложения indexing так:
class C inherit -- [1]
или
class C feature -- [2]
или
class -- [3]
C
feature
Нельзя писать
class C -- [4] feature
поскольку первая строка нарушает правило.
Формы [1] и [2] используются в этой книге для небольших иллюстративных классов. Более практичные классы имеют одно или несколько помеченных предложений feature, они в отсутствие предложения inherit должны использовать форму [3] (она предпочтительнее, чем форма [2]):
class
C
feature -- Initialization
...
feature -- Access
и т.д.
Подобно большинству современных языков, наша нотация не придает особого значения окончаниям строк за исключением строк, завершающихся комментарием. Две или более инструкций (объявления) могут располагаться на одной строке, разделенные в этом случае точками с запятой:
count := count + 1; forth
Этот стиль по ряду причин не очень популярен (многие инструментальные средства для оценки размера ПО используют строки, а не синтаксические единицы); большинство разработчиков предпочитают располагать не более одной инструкции на строку. Действительно, нежелательно упаковывать текст, но в ряде случаев удобно и разумно располагать ряд связанных инструкций на одной строке.
В этой области лучше полагаться на ваши предпочтения и хороший вкус. Если вы применяете внутристрочное группирование, убедитесь, что оно остается умеренным и согласованным с внутренними отношениями между инструкциями. Принцип Точки с Запятой, который появится чуть позже, требует разделения таких инструкций точкой с запятой.
По очевидным причинам объема эта книга широко использует внутристрочное группирование, согласованное со сделанными рекомендациями. Она также избегает расщепления многострочных инструкций на число строк, больше строго необходимого. Нужно лишь помнить, что в независимости от персонального вкуса следует соблюдать гребенчатую структуру.
Гребенчатая структура использует отступы, для создания которых используется табуляция (но не пробелы!).
Вот какова иерархия отступов для основных видов конструкции, иллюстрируемых ниже следующим примером:
indexing (начинающее предложение class (начинающее тело класса), feature (начинающее предложение feature, исключая случай, когда feature находится на той же строке, что и class ), invariant (начинающее предложение инварианта) и заключительный end класса.declaration ; разделы индексирования; предложения инварианта.require, local, do, once, ensure, rescue, end.Внутри тела программы может быть своя система отступов при гнездовании управляющих структур. Например, инструкция if a then... содержит две ветви, каждая с отступом. Эти ветви могут сами содержать инструкции цикла или выбора, приводящие к дальнейшему гнездованию. Еще раз заметим, что ОО-стиль этой книги приводит к простым подпрограммам, редко приводящим к высокому уровню гнездования.
Инструкция check задается с отступом. За ней, как правило, следует поясняющий комментарий, располагаемый на следующем уровне справа от охраняемой инструкции.
indexing
description: "Пример форматирования"
class EXAMPLE inherit
MY_PARENT
redefine f1, f2 end
MY_OTHER_PARENT
rename
g1 as old_g1, g2 as old_g2
redefine
g1
select
g2
end
creation
make
feature -- Initialization
make is
-- Сделать нечто
require
some_condition: correct (x)
local
my_entity: MY_TYPE
do
if a then
b; c
else
other_routine
check max2 > max1 + x ^ 2 end
-- Из постусловия другой подпрограммы
new_value := old_value / (max2 - max1)
end
end
feature -- Access
my_attribute: SOME_TYPE
-- Объяснение его роли (выровнено с комментарием для make)
... Объявления других компонентов и предложения feature ...
invariant
upper_bound: x <= y
end --class Example
Белые пробелы (пробелы, табуляция, символы окончания строки) производят такой же эффект в программных текстах, как паузы в нотной записи.
Общее правило состоит в следовании, насколько это возможно, общепринятой практике обычного письменного языка. По умолчанию таковым языком является английский, хотя возможна адаптация правил к другим языкам.
Вот некоторые из следствий. Используйте пробелы:
f (x) (но не f(x) в стиле C, или f(x) ).proc1 (x); x := f1 (x) + f2 (y).g (x, y, z).Аналогично, по умолчанию пробел ставится после, но не перед точкой с запятой:
p1; p2 (x); p3 (y, z)
Однако некоторые люди предпочитают французский стиль написания, согласно которому пробелы ставятся и до и после точки с запятой:
p1 ; p2 (x) ; p3 (y, z)
Выбирайте любой стиль, но применяйте его согласованно. (Эта книга использует английский стиль.) Английский и французский стили отличаются и для двоеточий. И здесь английский стиль предпочтительнее, как в your_entity: YOUR_TYPE.
Пробелы должны появляться до и после арифметических операций, как в a + b. (В этой книге из экономии пробелы могут опускаться, например в выражении n+1.)
Для точек нотация отходит от соглашений, принятых в естественном языке, поскольку точки используются в специальной конструкции, первоначально введенной в языке a.r означает: применить компонент r к объекту, присоединенному к a. Здесь не должно быть никаких пробелов ни до, ни после точки. В вещественных числах, таких как 3.14, используется обычная точка.
Соглашения о приоритетах в нотации соответствуют традициям и принципу Наименьших Сюрпризов во избежание ошибок и двусмысленностей.
Для ясности добавляйте скобки без колебаний; например, вы можете написать (a = (b + c)) implies (u /= v) несмотря на то, что смысл этого выражения не изменится, если все скобки будут опущены. В примерах этой книги зачастую расставлены "лишние" скобки, особенно в утверждениях, возможно, утяжеляя выражение, но избегая неопределенности.
С давних пор два равно известных клана живут в компьютерном мире и вражда между ними столь же ожесточена, как и в
Пропагандистские машины обеих сторон приводит бесконечные аргументы в свою пользу. Культом Терминалистов является единообразие: если каждая инструкция завершается одним и тем же маркером, никто и не посмеет задавать вопрос "должен ли я здесь ставить точку с запятой?" (ответ в языках Терминалистов всегда - да, и всякого, кто нарушит предписание, ждет кара за измену). Они не хотят, чтобы нужно было удалять или добавлять этот символ при изменении местоположения инструкции, например, удаляя или внося ее в тело инструкции выбора.
Сепаратисты возносят хвалу элегантности их соглашения и его совместимости с математической практикой. Они рассматривают do instruction1; instruction2; instruction3 end как естественного родственника f (argument1, argument2, argument3). Кто в здравом уме, спрашивают они, предпочел бы писать f (argument1, argument2, argument3,) с ненужной заключительной запятой? Более того, они утверждают, что Терминалисты фактически являются защитниками Компиляторщиков, жестоких людей, чьей единственной целью является обеспечение легкой жизни для разработчиков компиляторов, даже если это приведет к трудной жизни разработчиков приложений.
Сепаратисты должны постоянно бороться с инсинуациями, например, что их языки не позволяют лишних точек с запятой. Снова и снова они должны повторять истину: что каждый язык, заслуживающий этого имени, начиная с признанного патриарха этого племени,
a; b; c a; b; c; ; a ;; b ;;; c;
Все строки здесь синтаксически правильны и эквивалентны. Они отличаются лишь наличием пустых инструкций в двух последних строках, которые любой уважающий себя компилятор спокойно удалит. Они указывают, насколько терпимее их соглашение, чем правило фанатичных соперников, когда каждая пропущенная точка с запятой является поводом для атак. Они же готовы принять столько точек с запятой, сколько Терминалисты в силу привычки захотят добавить в тексты Сепаратистов.
Методы современной пропаганды нуждаются в научном обосновании и статистике. В 1975 году Терминалисты провели исследование ошибок, для чего две группы программистов по 25 человек в каждой использовали языки, отличающихся, среди прочего, соглашениями о точках с запятой. Его результаты широко цитировались и послужили оправданием терминалистского соглашения в языке
Взволнованные непрекращающейся вражеской пропагандой лидеры Сепаратистов обратились за помощью к автору настоящей книги, который, к счастью, вспомнил давно забытый принцип: цитаты хороши, но лучше прочитать первоисточник. Обратившись к оригинальной статье, он обнаружил, что язык Сепаратистов, используемый в сравнении, представлял мини-язык, предназначенный только для обучения студентов концепциям асинхронных процессов, в котором лишняя точка с запятой, как в begin a; b; end, рассматривалась как ошибка! Ни один реальный язык Сепаратистов, как отмечалось выше, такого правила не имеет. Из контекста статьи следовало также, что студенты, участвующие в эксперименте, имели предыдущий опыт работы с языком PL/I (Терминалистов) и посему имели привычки ставить точки с запятыми повсюду. Так что результаты этой статьи не дают никаких оснований отдать предпочтение Терминализму в ущерб Сепаратизму.
Для некоторых других проблем, изучаемых в этой статье, таких пороков не замечено, так что она представляет интерес для людей, интересующихся проектированием языков программирования. |
Все это показывает, что в таком чувствительном вопросе опасно принять точку зрения одной из сторон, особенно если хочешь сохранить друзей из обоих лагерей. Решение, принятое в нотации этой книги, радикально:
Правило Синтаксиса Точек с Запятой Точки с запятой, как маркеры, ограничивающие инструкции, объявления и утверждения, возможны почти во всех позициях, где они могут появляться. |
"Почти", поскольку в редких случаях, не встречаемых в этой книге, пропуск точки с запятой может стать причиной синтаксической неоднозначности. |
Правило Синтаксиса Точек с запятой означает, что вы можете выбрать любой стиль:
Это одна из тех областей, где предпочтительно позволять каждому пользователю следовать своему собственному стилю, поскольку его выбор не может стать причиной серьезных нарушений. Но при этом внутри одного класса или лучше внутри одной библиотеки классов следует придерживаться единого стиля, соблюдая следующий принцип:
Принцип Стиля Точки с Запятой Если вы предпочитаете рассматривать точку с запятой как заключительную часть инструкции, (стиль Терминалиста), поступайте так для всех применимых элементов. Если вы рассматриваете точку с запятой как разделитель, используйте ее только тогда, когда она синтаксически неизбежна или при разделении элементов, появляющихся на одной и той же строке. |
Второе предложение управляет случаем двух или более элементов в одной строке:
found := found + 1; forth
Здесь точка с запятой должна всегда присутствовать. Ее пропуск будет ошибкой.
В этом обсуждении не дается совет, какой из четырех стилей является предпочтительным. Первое издание этой книги использовало стиль Сепаратистов. Но затем в обсуждениях с коллегами и опытными пользователями я обнаружил (помимо небольшой горстки Терминалистов) почти равное число Сепаратистов и Минималистов. Некоторые из Минималистов были весьма убедительными, в частности, университетский профессор, заявивший, что главная причина, по которой его студенты предпочитают данную нотацию, что в ней не обязательны точки с запятыми, - комментарий, который любой будущий проектировщик языка, при всех его грандиозных планах, должен найти поучительным или, по крайней мере, здравым.
Следует полагаться на свой вкус, пока он согласован и соответствует принципу Стиля Точки с Запятой. (Что же касается этой книги, то вначале скорее по привычке, чем по реальной привязанности я придерживался стиля Сепаратистов, но затем, наслушавшись призывов начать новую жизнь с приходом третьего тысячелетия и порвать со старыми привычками, я удалил все точки с запятыми в течение одной ночи сплошного разгула.)
Следует именовать утверждения для большей читабельности текста:
require
not_too_small: index >= lower
Это соглашение способствует созданию полезной информации при тестировании и отладке, поскольку, как вы помните (см. лекцию 11 курса "Основы объектно-ориентированного программирования"), метка утверждения включается в сообщение периода выполнения, создаваемое при нарушениях утверждений при включенном их мониторинге.
Это соглашение распространяется на утверждения, состоящих из нескольких предложений, расположенных на разных строках. В данной книге, опять-таки по соображениям экономии объема, метки опускаются, когда несколько утверждений располагаются на одной строке:
require
index >= lower; index <= upper
При нормальных обстоятельствах лучше придерживаться официального правила и иметь по одному помеченному предложению утверждения на каждой строке текста.
При наборе программных текстов рекомендуются следующие соглашения, используемые в этой книге и связанных публикациях.
Используйте для программных элементов (имен классов, компонентов, сущностей и так далее) курсив. Это облегчает их включение в предложения обычного текста, как, например, "Можно видеть, что компонент number является запросом, а не атрибутом". (Слово number означает имя компонента, и вы не хотите, чтобы читатель мог подумать, что речь идет о числе компонентов!)
Ключевые слова, такие как class, feature, и другие, набираются полужирным шрифтом ( boldface ).
Ключевые слова играют чисто синтаксическую роль: они не имеют собственной семантики. Как отмечалось ранее, есть несколько зарезервированных слов, не являющихся ключевыми, таких как Current и Result, обладающих семантикой выражений или сущностей. Они пишутся курсивом с начальным символом в верхнем регистре.
Следуя традициям математики, разделители - двоеточия, запятые, различные скобки и другие - всегда появляются прямыми (шрифтом roman), даже если они стоят после
Текст комментария пишется прямым (roman) шрифтом. Имена программных элементов, в соответствии с ранее введенным правилом, даются в комментариях, курсивом. Например:
accelerate (s: SPEED; t: REAL) is
-- Развить скорость s за максимум t секунд
...
set_number (n: INTEGER) is
-- Сделать n новым значением number
...
В самих программных текстах, где невозможны вариации шрифта, такие вхождения формальных элементов в комментарии должны следовать соглашениям, уже упоминавшимся ранее: они появляются в одинарных кавычках
-- Сделать 'n' новым значением 'number'
(Заметьте, следует использовать разные символы для открывающей и закрывающей кавычки.) Инструментальные средства, обрабатывающие текст класса, такие как short и , знают об этом соглашении и при печати выводят закавыченные элементы курсивом.
Предыдущие соглашения о шрифтах хорошо работают для книг, статей, Web-страниц. В некоторых контекстах могут применяться другие подходы. Так при показе слайдов через проектор элементы, записанные курсивом, иногда и полужирным курсивом, не всегда читаются на экране.
В таких случаях я использую следующие соглашения:
Все формальные элементы появляются в
Книга [Walden 1995] послужила источником примера, демонстрирующего, что подчеркивание в многословных идентификаторах separated_by_underscores предпочтительнее использования букв верхнего регистра internalUpperCase.
В [Gannon 1975] описан эксперимент по изучению влияния языковых предпочтений на уровень программистских ошибок.
Правила стандартного именования компонентов были впервые представлены в [M 1990b] и в деталях разработаны в [1994a].
Я получил важные комментарии от Рихарда Винера о студентах, оценивших возможность опускать точки с запятыми, и Кима Уолдена по поводу курсива и полужирного начертания.
Перепишите следующий заголовочный комментарий в более подходящем стиле:
reorder (s: SUPPLIER; t: TIME) is
-- Повторно заказывает текущую деталь у поставщика s,
-- которую следует доставить до достижения срока t;
-- эта программа работает только при условии,
-- что срок поставки еще не истек
require
not_in_past: t >= Now
...
next_reorder_date: TIME is
-- Выдает следующий срок, к которому текущая деталь
-- должна быть повторно заказана
Можете ли вы придумать случай, при котором пропуск точки с запятой между двумя инструкциями или утверждениями станет причиной синтаксической неоднозначности, или, по меньшей мере, создавал бы помехи наивному грамматическому разбору?
Подсказка: компонент может иметь в качестве цели выражение в скобках, как в (vector1 + vector2).count.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.