KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Прочая околокомпьтерная литература » Бертран Мейер - Основы объектно-ориентированного программирования

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

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Бертран Мейер, "Основы объектно-ориентированного программирования" бесплатно, без регистрации.
Перейти на страницу:

Эта схема применима ко многим процедурам set_attribute. В классе DEVICE имеем:


class DEVICE feature

alternate: like Current

set_alternate (a: like Current) is

-- Пусть a - альтернативное устройство.

do

alternate := a

end

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

end



Еще раз о базовых классах

С введением закрепленных типов нуждается в расширении понятие базового класса типа.

Сначала классы и типы были для нас едины, и это их свойство - отправной пункт ОО-метода, - по существу, сохраняется, хотя нам пришлось немного расширить систему типов, добавляя в классы родовые параметры. Каждый тип основан на классе и для типа определено понятие базового класса. Для типов, порожденных универсальным классом с заданными фактическими родовыми параметрами, базовым классом является универсальный класс, в котором удалены фактические параметры. Так, например, для LIST [INTEGER] базовым классом является LIST. На классах основаны и развернутые типы; и для них аналогично: для expanded SOME_CLASS [...] базовый класс - SOME_CLASS.

Закрепление типов - это еще одно расширение системы типов, которое, подобно двум предыдущим, сохраняет свойство выводимости каждого типа непосредственно из класса. Базовым для like anchor является базовый класс типа сущности anchor в текущем классе. Если anchor есть Current, базовым будет класс, в котором это объявление содержится.

Правила о закрепленных типах

Теоретически ничто не мешает нам записать like anchor для самого элемента anchor как сущности закрепленного типа. Достаточно ввести правило, которое запрещало бы циклы в декларативных цепочках.

Вначале закрепленные опорные элементы (anchored anchor) были запрещены, но это новое, более либеральное правило придает системе типов большую гибкость.

Пусть T - тип anchor (текущий класс, если anchor есть Current). Тогда тип like anchor совместим как с самим собой, так и с T.

Обратное определение не симметрично: единственный тип, совместимый с like anchor, - это он сам. В частности, с ним не совместим тип T. Если бы следующий код был верен:


anchor, other: T; x: like anchor

...

create other

x := other -- предупреждение: ошибочное присваивание



то в порожденном классе, где anchor меняет свой тип на U, совместимый с T, но основанный на его потомке, сущности x был бы присвоен объект типа T, а не объект типа U или U-совместимого типа, что некорректно.

Будем говорить, что x опорно-эквивалентен y, если x есть y или имеет тип like z, где z по рекурсии опорно-эквивалентен y. Присваивания: x := anchor, anchor := x, как и присваивания опорно-эквивалентных (anchor-equivalent) элементов, конечно же, допустимы.

При закреплении формального параметра или результата, как в случае


r (other: like Current)



фактический параметр вызова, например, b в a.r(b), должен быть опорно-эквивалентен a.

Когда не используются закрепленные объявления

Не всякое объявление вида x: A в классе A следует менять на x: like Current и не в каждой паре компонентов одного типа следует один из них делать опорным, а другой - закрепленным.

Закрепленное объявление - это своего рода обязательство изменения типа закрепленной сущности при смене типа опорного элемента. Как мы видели, оно не имеет обратной силы: объявив тип сущности как like anchor, вы теряете право на переопределение его в будущем (коль скоро новый тип должен быть совместим с исходным, а с закрепленным типом совместим только он сам). Пока не введено закрепление, остается свобода: если x типа T, то потомок может переопределить тип, введя более походящий тип U.

Достоинства и недостатки закрепления сущностей очевидны. Закрепление гарантирует, что вам не придется выполнять повторные объявления вслед за изменением типа опорного элемента, но оно раз и навсегда привязывает вас к типу опорного элемента. Это типичный случай "свободы выбора". (В каком-то смысле Фауст объявил себя like Мефистофель.)

Как пример нежелательного закрепления рассмотрим компонент first_child для деревьев, описывающий первого сына данного узла дерева. (При построении дерева он аналогичен компоненту first_element для списков, типом которого изначально является CELL [G] или LINKABLE [G].) Для деревьев требуется повторное объявление. Может показаться, что уместным использовать закрепленное объявление:


first_child: like Current



Но на практике это накладывает слишком много ограничений. Класс дерева может иметь потомков, представляющих разные виды деревьев (их узлов): UNARY_TREE (узлы с одним сыном), BINARY_TREE (узлы с двумя сыновьями) и BOUNDED_ARITY_TREE (узлы с ограниченным числом сыновей). При закреплении first_child все сыновья каждого узла должны иметь один и тот же отцовский тип.

Это может быть нежелательным при построении более гибких структур, например бинарного узла с унарным потомком. Для этого компонент нужно описать без закрепления:


first_child: TREE [G]



Это решение не связано с какими-то ограничениями, и для создания деревьев с узлами одного типа вы, оставив класс TREE без изменений, можете породить от него HOMOGENEOUS_TREE, где переопределить first_child как


first_child: like Current



что гарантирует неизменность типов всех узлов дерева.

Статический механизм

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

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

Наследование и скрытие информации

Последний вопрос, оставшийся пока без ответа, как наследование взаимодействует с принципом Скрытия информации.

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

Кое-что о политике

Что происходит со статусом экспорта при передаче компонента потомку? Наследование и скрытие информации - ортогональные механизмы. Наследование определяет отношение между классом и его потомками, экспорт - между классом и его клиентами. Класс B может свободно экспортировать или скрывать любой из компонентов f, унаследованных им от класса A. При этом доступны все возможные комбинации:

[x]. f экспортируется в классе A и в классе B (хотя и не обязательно одним и тем же клиентам);

[x]. f скрыто в A и B;

[x]. f скрыто в A, но полностью или частично экспортируется в B;

[x]. f экспортируется в A, но скрыто в B.

Правило гласит: по умолчанию f сохраняет тот статус экспорта, которым компонент был наделен в A. Однако его можно изменить, добавив предложение export в предложение наследования класса. Например:


class B inherit

A

export {NONE} f end

-- Скрыть f (возможно, экспортируемый в классе A)

...



или


class B inherit

A

export {ANY} f end

-- Экспортировать f (возможно, скрытый в классе A)

...



или


class B inherit

A

export {X, Y, Z} f end

-- Сделать f доступным определенным классам

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*