Чтение онлайн

ЖАНРЫ

Основы объектно-ориентированного программирования

Мейер Бертран

Шрифт:

[x]. компонентам низкоуровневой системы может понадобиться объем доступной им памяти, выделенный средой при инициализации;

[x]. система эмуляции терминала может начать работу с отправки среде запроса о числе терминальных портов. Затем эти данные будут использоваться в ряде модулей приложения.

Такие глобальные данные аналогичны совместно используемым объектам, хотя обычно они являются значениями базовых типов. Схема их реализации однократными функциями такова:

Const_value: T is

– - Однократно вычисляемый системный параметр

local

envir_param: T ' -- Любой тип (T и не только)

once

"Получить envir_param из операционной среды"

Result := "Значение, рассчитанное на основе envir_param"

end

Такие однократные функции описывают динамически вычисляемые константы.

Предположим, данное объявление находится в классе ENVIR. Класс, которому надо воспользоваться константой Const_value, получит ее значение, указав ENVIR в списке своих родителей. В отличие от классического подхода к расчету константы, здесь не нужна процедура инициализации системы, вычисляющая все глобальные параметры системы, как это делается в классическом подходе. Как отмечалось в начальных лекциях, такая процедура должна была бы иметь доступ к внутренним деталям многих модулей, что нарушало бы ряд критериев и принципов модульности: декомпозиции, скрытия информации и других. Наоборот, классы, подобные ENVIR, могут разрабатываться как согласованные модули, каждый задающий множество логически связанных глобальных значений. Процесс вычисления такого параметра, к примеру, Const_value, инициирует первый из компонентов, который запросит этот параметр при выполнении системы. Хотя Const_value является функцией, использующие его компоненты могут полагать, что имеют дело с константным атрибутом.

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

Однократные процедуры

Функция close должна вызываться только один раз. Контроль над количеством ее вызовов рекомендуется возложить на глобальную переменную приложения.

Из руководства к коммерческой библиотеке функций языка C

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

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

Однократные процедуры решают эту проблему лучше:

check_setup is

– - Настроить терминал, если это еще не сделано.

once

terminal_setup -- Фактические действия по настройке.

end

Теперь каждая экранная функция должна начинаться с обращения к check_setup, первый вызов которой приведет к настройке параметров, а остальные не сделают ничего. Заметьте, что check_setup не должна экспортироваться клиентам.

Однократная процедура - это важный прием, упрощающий применение библиотек и других программных пакетов.

Параметры

Однократные процедуры и функции могут иметь параметры, необходимые, по определению, лишь при первом вызове.

Однократные функции, закрепление и универсальность

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

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

Начнем с универсальности. Пусть в родовом классе EXAMPLE [G] есть однократная функция, чей тип родовой параметр:

f: G is once ... end

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

character_example: EXAMPLE [CHARACTER]

...

print (character_example.f)

Пока все в порядке. Но если попытаться получить константу с другим родовым параметром:

integer_example: EXAMPLE [INTEGER]

...

print (integer_example.f + 1)

В последней инструкции мы складываем два числа. Первое значение, результат вызова f, к сожалению, уже найдено, поскольку f– однократная функция, причем символьного, а не числового типа. Сложение окажется недопустимым.

Проблема заключается в попытке разделения значения разными формами родового порождения, ожидающими значения, тип которого определяется родовым параметром. Аналогичная ситуация возникает и с закреплением типов. Представим себе класс B, добавляющий еще один атрибут к компонентам своего родителя A:

class B inherit A feature

attribute_of_B: INTEGER

end

Пусть A имеет однократную функцию f, возвращающую результат закрепленного типа:

f: like Current is once create Result make end

и пусть первый вызов функции f имеет вид:

a2 := a1.f

где a1 и a2 имеют тип A. Вычисление f создаст экземпляр A и присоединит его к сущности a2. Все прекрасно. Но предположим, далее следует:

Поделиться с друзьями: