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

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

Парадокс расширения-специализации

Наследование иногда рассматривается как расширение, а иногда как специализация. Хотя эти два толкования как будто противоречат друг другу, оба они истинны - но с разных точек зрения.

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

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

>Здесь мы говорим о реализуемых компонентах, а не о предлагаемых (клиентам) службах, потому что при соединении скрытия информации с наследованием, как мы увидим, B может скрыть от клиентов некоторые из компонентов, в то время как A их экспортировал своим клиентам.

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

Парадокс расширения-специализации - это одна из причин для устранения термина 'подкласс', предполагающего понятие 'подмножество'. Другой, уже отмеченной, является встречающееся в литературе сбивающее с толку использование термина 'подкласс' для обозначения как прямого, так и непрямого наследования. Эти проблемы не возникают при использовании точно определенных терминов: наследник, потомок и собственный потомок и двойственных к ним терминов: родитель, предок и собственный предок.

Роль отложенных классов

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

Назад к абстрактным типам данных

Насыщенные утверждениями отложенные классы хорошо подходят для представления АТД. Прекрасный пример - отложенный класс для стеков. Мы уже описывали процедуру put, сейчас приведем возможную версию полного описания этого класса.

indexing

description:

'Стеки (распределительные структуры с дисциплиной Last-in, First-Out), %

%не зависящие от выбора представления'

deferred class

STACK [G]

feature -- Доступ

count: INTEGER is

-- Число элементов.

deferred

end

item: G is

-- Последний вставленный элемент.

require

not_empty: not empty

deferred

end

feature - Отчет о статусе

empty: BOOLEAN is

-- Стек пустой?

do

Result := (count = 0)

end

full: BOOLEAN is

-- Стек заполнен?

deferred

end

feature - Изменение элемента

put (x: G) is

-- Втолкнуть x на вершину.

require

not full

deferred

ensure

not_empty: not empty

pushed_is_top: item = x

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату