Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по основам ООП.docx
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
5.29 Mб
Скачать

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

Не всякое объявление вида 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 доступным определенным классам

...