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

Шрифт
Интервал


>class FILE feature

>error_code: INTEGER; -- Атрибут-переменная

>Ok: INTEGER is 0

>Open_error: INTEGER is 1

>...

>open (file_name: STRING) is

>-- Открыть файл с именем file_name

>-- и связать его с текущим файловым объектом

>do

>error_code := Ok

>...

>if "Что-то не так" then

>error_code := Open_error

>end

>end

>... Прочие компоненты ...

>end



Клиент может вызвать метод open и проверить успешность операции:


>f: FILE; ...

>f.open

>if f.error_code = f.Open_error then

>"Принять меры"

>else

>...

>end



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


>class EDITOR_CONSTANTS

>feature

>Insert: CHARACTER is 'i'

>Delete: CHARACTER is 'd'; -- и т.д.

>...

>end

>class SOME_CLASS_FOR_THE_EDITOR

>inherit

>EDITOR_CONSTANTS

>...Другие возможные родители ...

>feature ...

>... подпрограммы класса имеют доступ к константам, описанным в EDITOR_CONSTANTS ...

>end



Класс, подобный EDITOR_CONSTANTS, служит лишь для размещения в нем группы констант, и его роль как "реализации АТД" (а это - наше рабочее определение класса) не столь очевидна, как в предыдущих примерах. Теоретическое обоснование введения таких классов мы обсудим позднее. Представленная схема работоспособна только при множественном наследовании, поскольку классу SOME_CLASS_FOR_THE_EDITOR могут потребоваться и другие родители.

Константы пользовательских классов

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

Константы с манифестом для этого непригодны

Первым примером служит класс, описывающий комплексное число:


>class COMPLEX creation

>make_cartesian, make_polar

>feature

>x, y: REAL

>-- Действительная и мнимая часть

>make_cartesian (a, b: REAL) is

>-- Установить действительную часть a, мнимую - b.

>do

>x := a; y := b

>end

>... Прочие методы (помимо x и y, других атрибутов нет) ...

>end



Пусть мы хотим определить константу - комплексное число i, действительная часть которого равна 0, а мнимая 1. Первое, что приходит в голову, - это буквальная константа вида


>i: COMPLEX is "Выражение, определяющее комплексное число (0, 1)"



Как записать выражение после is? Для пользовательских типов данных никакой формы записи неименованных констант не существует.

Можно представить себе вариант нотации на основе атрибутов класса:


>i: COMPLEX is COMPLEX (0, 1)



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

Кроме того, как гарантировать соответствие неименованной константы инварианту класса, если таковой имеется?

Последнее замечание позволяет найти правильное решение. Мы уже говорили о том, что в момент рождения объекта ответственность за соблюдение инварианта возлагается на процедуру создания. Создание объекта иным путем (помимо безопасного клонирования clone) ведет к ситуациям ошибки. Поэтому мы должны найти путь, основанный на обычном методе создания объектов класса.

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

Пусть константный объект - это функция. Например, i можно (в иллюстративных целях) описать внутри самого класса COMPLEX как


>i: COMPLEX is

>-- Комплексное число, re= 0, а im= 1

>do

>create Result.make_cartesian (0, 1)

>end



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

Однако результат не соответствует потребностям: каждое обращение клиента к i порождает новый объект, идентичный всем остальным, а это - трата времени и пространства. Поэтому необходим особый вид функции, выполняемой только при первом вызове. Назовем такую функцию однократной (once function). В целом она синтаксически аналогична обычной функции и отличается лишь служебным словом once, начинающего вместо do ее тело:


>i: COMPLEX is

>-- Комплексное число, re= 0, im= 1

>once

>create Result.make_cartesian (0, 1)

>end



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

Результат, найденный при первом вызове однократной функции, может использоваться во всех экземплярах класса, включая экземпляры потомков, где эта функция не переопределена. Переопределение однократных функций как обычных (и обычных как однократных) допускается без всяких ограничений. Так, если COMPLEX1, порожденный от класса COMPLEX, заново определяет i, то обращение к i в экземпляре COMPLEX1