Docu/ru/BB-Docu.odc

Соглашения по документации
ENGLISH
Редакция перевода: Ф.В.Ткачев, 2009-10-17; 2012-11-27

Документация преследует две главные цели: сообщить, как могут быть интерактивно использованы возможности программы (руководство пользователя) и как возможности программы могут быть использованы программно (руководство разработчика). 
В системе Блэкбокс обслуживающие программы оформлены в подсистемы. 
Обычно подсистема состоит из нескольких модулей, возможно, взаимодействующих. 
В общем случае нужны три типа документации: руководство пользователя, описание интерфейса для каждого модуля, а также обзор того, как модули могут взаимодействовать друг с другом, т.е. введение в общую архитектуру подсистемы (руководство разработчика).
Подсистемы представлены на диске как каталоги, например, каталог Text. 
В этом каталоге содержится вложенный каталог под названием Docu, т.е. Text/Docu. 
Этот каталог с документацией может содержать следующие текстовые документы:
* Руководство пользователя, называемое User-Man.
* Карта гиперссылок на документацию, называемая Sys-Map.
* Вводная информация для программиста под названием Dev-Man.
* По одному описанию интерфейса на каждый модуль, под именем модуля без префикса подсистемы, например: Views для модуля TextViews.
Следует отметить, что все тексты документации, не являющимися непосредственными справочниками для программиста по конкретному модулю, должны содержать в имени дефис (-), чтобы обозреватель хранилища [repository browser] мог отличить документацию по модулям от прочих файлов документации.
Остальная часть данного текста описывает формат описания интерфейса модуля.
Каждое описание модуля начинается с определения модуля. Для описания модулей используется нотация Компонентного Паскаля, и в описании перечисляются все те и только те элементы модуля, которые экспортируются. Определение модуля начинается с ключевого слова DEFINITION вместо MODULE, метки экспорта опускаются (за исключением метки только для чтения); также опускаются тела процедур и тело модуля. Методы показываются в объявления соответствующей записи без ключевого слова PROCEDURE.
Примером служит гипотетический модуль Dictionaries

DEFINITION Dictionaries;

	CONST maxElems = 32;

	TYPE
		Dictionary = POINTER TO LIMITED RECORD
			elems-: INTEGER;
			(d: Dictionary) Put (string: ARRAY OF CHAR; OUT key: INTEGER), NEW;
			(d: Dictionary) Get (key: INTEGER; OUT string: ARRAY OF CHAR), NEW
		END;

	VAR someDict: Dictionary;

	PROCEDURE Init;

END Dictionaries.

В расширенной записи не указаны унаследованные процедуры, только новые процедуры или процедуры с измененными сигнатурами или уточненными спецификациями семантики. Изменение сигнатуры может быть вызвано ковариантным результатом функции, т.е. если процедура возвращает указатель на расширение типа, возвращаемого базовой процедурой. Или это может быть вызвано изменением атрибутов метода (конечный, ABSTRACT, EMPTY, EXTENSIBLE), например, мог быть реализован и сделан конечным абстрактный метод.
За описанием модуля следует краткое описание абстракций, предоставляемых модулем.

Модуль предоставляет некий конкретный тип Dictionary для работы со словарями цепочек литер.

Затем следует список констант без их значений.

CONST maxElems
Максимальное число элементов словаря.

Затем следуют типы модуля:

TYPE Dictionary
LIMITED
Управляет словарем литерных цепочек с целочисленным ключом.

Атрибуты записи приводятся во второй строке, как показано выше. Атрибуты записи могут отсутствовать (запись является конечной по умолчанию) или быть EXTENSIBLE, ABSTRACT или LIMITED.
Если тип является расширением другого типа, то это показывается следующим образом: 
Dictionary (OtherModule.Dictionary).
Затем перечисляются поля записи:

elems-: INTEGER	0 <= elems <= maxElems
Текущее количество элементов в словаре.

Как и в вышеприведенном примере, может также быть приведен инвариант для значений полей записи.
Затем приводятся процедуры, связанные с данным типом:

PROCEDURE (d: Dictionary) Put (string: ARRAY OF CHAR; OUT key: INTEGER)
NEW

Для каждого метода уточняется, является ли процедура конечной (final, по умолчанию), расширяемой (extensible), абстрактной (abstract) или пустой (empty). 
Абстрактные процедуры не должны вызываться. 
Пустые процедуры не должны вызываться в расширяющей их процедуре (т.е. посредством супервызова в реализации процедуры).
Во второй строке даются атрибуты метода, которые могут либо отсутствовать (метод является конечным по умолчанию), либо один из EXTENSIBLE, ABSTRACT или EMPTY. Для вновь вводимых методов перед этим атрибутом указывается NEW.
Методы, экспортируемые только для реализации (implement-only), могут быть вызваны только внутри своего определяющего модуля. Этот модуль может, таким образом, обеспечить предусловия для метода перед каждым его вызовом, поскольку все такие вызовы происходят в этом модуле. При корректной реализации эти предусловия никогда не могут нарушиться, так как другие модули не могут вызвать метод. Это позволяет проектировщику расширений предполагать, что предусловия выполнены, и проверять их нет необходимости. Поэтому предусловиям в методах, экспортированных только для реализации, не приписываются номера ошибок, вместо этого они помечаются как гарантированные, то есть описываемый модуль ручается за них.
Затем следует разъяснение действий процедуры:

Поместить цепочку в словарь d и получить в ответ key. Если цепочка уже есть в словаре, то возвращается тот же ключ, который был возвращен при первой вставке. Если строки еще нет в словаре, то она туда вставляется и возвращается еще не занятое значение ключа. 
После разъяснений простым текстом могут быть указаны некоторые предусловия для процедуры. Эти предусловия приводятся в полуформальной нотации:

Pre
	20	string # ''

Это означает, что если предусловие нарушается (т.е. string = ''), то выполняемая в данный момент команда прерывается с номером исключения 20. Вместо номера могут также быть указаны следующие сообщения:

	invalid index
	type guard check

Поэтому второе предусловие могло бы быть таким:

invalid index string in d or d.elems < maxElems
		
В более старых частях документации номера предусловий могут быть даны после выражения, а не перед ним. Со временем вся документация должна быть приведена к одному новому, более удобочитаемому стилю.
После предусловий могут быть указаны некоторые постусловия:

	Post
		string in d'
			old key returned
		~(string in d')
			string in d
			d.elems = d.elems' + 1
			new key returned
				
Это постусловие содержит два случая, а именно, что происходит, когда цепочка уже содержится в d, и что присходит, если цепочки в d еще нет. Текст условий выровнен по левому краю, а соответствующие результаты сдвинуты вправо на один отступ. Иногда могут быть использованы вложенные условия с дальнейшим отступом.
Для ссылки на значения перед выполнением процедуры к выражению добавляется апостроф, если это нужно для ясности (т.е. если значение вообще может измениться). Так, выражение

	d.elems = d.elems' + 1
	
означает, что значение d.elems увеличилось на единицу.
В общем, для подобных выражений используется синтаксис Компонентного Паскаля, но допускаются послабления, например, могут использоваться простые вспомогательные процедуры, в дальнейшем не описываемые.
Результаты функций в общем случае называются result. 
Объем формализма остается небольшим. Делается попытка ограничить формальные спецификации несколькими простыми условиями или объяснением особо тонких случаев. Не ставится цель дать полное формальное определение семантики библиотеки, поэтому формализм не может полностью заменить разъяснения, сделанные обычным текстом, примеры или графические иллюстрации.
Тем не менее, представляется, что проверки прежде всего предусловий часто оказываются полезными даже в таком неполном и полуформальном виде, как для целей документирования, так и для отладки.

PROCEDURE (d: Dictionsry) Get (key: INTEGER; OUT string: ARRAY OF CHAR)
NEW
Извлекает литерную цепочку из словаря по ключу key. Если значение не найдено параметру string присваивается пустое значение. 

Post
	key in d
		return corresponding string
	~(key in d)
		return ""

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

VAR someDict: Dictionary	someDict # NIL
Эта переменная содержит словарь.

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

PROCEDURE Init (d: Dictionary)
Инициализирует словарь. Любой словарь может быть размещен только один раз

Pre
	20	d	not yet inicialized
	
В Блэкбоксе все процедуры, чьи имена начинаются с Init, могут быть вызваны для объекта только один раз.