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

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

Заголовочные комментарии и предложения индексации

Хотя формальные элементы класса несут достаточно подробную информацию, следует сопровождать класс неформальными пояснениями. Заголовочные комментарии к подпрограммам и предложения 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

Как вы помните, класс может иметь несколько предложений 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 ), выбору представления ( representation ), информации об обновлениях ( revision ), информации об авторе и многому другому. В разделы включается все, что может облегчить понимание класса и поиск, использующий ключевые слова. Благодаря специальному инструментарию, поддерживающим повторное использование, облегчается задача разработчиков по поиску в библиотеках нужных им классов с нужными компонентами.

Как индексирующие термы, так и их значения могут быть произвольными, но возможности выбора фиксируются для каждого проекта. Множество стандартов, принятых в библиотеке Base, частично приведено в примере. Каждый класс должен иметь раздел description, значением которого index_value является строка, описывающая роль класса в терминах его экземпляров ( Последовательные списки..., но не "этот класс описывает последовательные списки", или "последовательный список", или "понятие последовательного списка" и т. д.). Для наиболее важных классов в этой книге - но не в коротких примерах, предназначенных для специальных целей - раздел description включался в предложение indexing.

Не заголовочные комментарии

Предыдущие правила применяются к стандартизованным комментариям, появляющимся в определенных местах и играющих специальную роль в документировании класса.

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

Есть еще одно использование комментариев, часто используемое на практике, но редко упоминаемое в учебниках. Я говорю здесь о технике преобразования некоторого участка кода в комментарий либо потому, что он не работает, либо он еще просто не готов. Эта практика, очевидно, требует замены специальными механизмами. Она уже обогатила язык новой глагольной формой - "закомментировать" (comment out).

Каждый комментарий по уровню абстракции должен быть выше комментируемого примера. Известный контрпример: -- Увеличить i на 1 в инструкции i := i + 1. Здесь комментарий является перефразировкой кода и не несет полезной нагрузки.

Языки низкого уровня призывают к подробному комментированию. Каждую строку C следует комментировать, поскольку в современной разработке языку C отводится роль инкапсуляции машинно-ориентированных операций и выполнения функций уровня операционной системы, что по своей природе является неким видом трюкачества и потому требует пояснений. В ОО-разработках комментарии, не относящиеся к заголовкам, встречаются значительно реже, они остаются необходимыми для тонких мест разработки и тогда, когда предвидится возможное смешение понятий. В своих постоянных усилиях предотвратить появление ошибок, а не лечить их последствия, метод уменьшает необходимость в комментариях благодаря модульному стилю, выработке небольших, понятных подпрограмм, через механизм утверждений. Предусловия и постусловия, инварианты класса формально выражают семантику, инструкции check выражают ожидаемые свойства, которые должны выполняться в определенном состоянии. Этому способствуют и соглашения именования, введенные в этой лекции. Общий тезис: секрет в создании ясного, понятного ПО состоит не в постфактумном добавлении комментариев, но в производстве согласованной и стабильной структуры системы, правильной с самого начала.