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

ЖАНРЫ

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

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

Шрифт:

... Интерфейс пакета ...

end VECTOR_HANDLING

Заметим, что наряду с типом G и подпрограммами родовым параметром служит значение zero– нулевой элемент сложения. Типичное использования пакета:

package BOOLEAN_VECTOR_HANDLING is

new VECTOR_HANDLING (BOOLEAN, "or", "and", false, true);

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

Являясь решением для Ada, данный прием не применим в объектной среде. Основа ОО-подхода - приоритет типов данных над операциями при декомпозиции ПО, чьим следствием является отсутствие независимых операций. Всякая операция принадлежит некоторому типу данных, основанному на классе. Следовательно, возникшая "на пустом месте" функция, скажем, infix "+", не может быть фактическим родовым параметром, стоящим в одном ряду с типами INTEGER и BOOLEAN. То же касается и значений, таких как zero и unity, обязанных знать свое место - быть компонентами класса - вполне респектабельными членами ОО-сообщества.

Ограничение родового параметра

Эти наблюдения дают решение. Мы должны оперировать исключительно терминами классов и типов.

Потребуем, чтобы любой фактический параметр, используемый классом VECTOR (в других примерах по аналогии), был типом, поставляемым с множеством операций: infix "+", zero для инициализации суммы и т.д. Владея наследованием, мы знаем, как снабдить тип нужными операциями, - нужно просто сделать его потомком класса, отложенного или эффективного, обладающего этими операциями.

Синтаксически это выглядит так:

class C [G -> CONSTRAINING_TYPE] ... Все остальное как обычно ...

где CONSTRAINING_TYPE– произвольный тип, именуемый родовым ограничением (generic constraint). Символ – > обозначает стрелку на диаграммах наследования. Результат этого объявления в том, что:

[x]. в роли фактических родовых параметров могут выступать лишь типы, совместимые с CONSTRAINING_TYPE;

[x]. в классе C над сущностью типа G допускаются только те операции, которые допускаются над сущностью CONSTRAINING_TYPE, другими словами, представляющими собой компоненты базового класса этого типа.

Какое родовое ограничение использовать для класса VECTOR? Обсуждая множественное наследование, мы ввели в рассмотрение NUMERIC– класс объектов, допускающих базисные арифметические операции: сложение и умножение с нулем и единицей (лежащая в его основе математическая структура называется кольцом). Эта модель кажется вполне уместной, хотя нам необходимо пока только сложение. Соответственно, класс будет описан так:

indexing

description: "Векторы, допускающие сложение"

class

VECTOR [G -> NUMERIC]

... Остальное - как и раньше (но теперь правильно!) ...

После чего ранее некорректная конструкция в теле цикла

Result.put(item (i) + other.item (i), i)

становится допустимой, поскольку item (i) и other.item (i) имеют тип G, а значит, к ним применимы все операции NUMERIC, включая, инфиксный "+".

Следующие родовые порождения корректны, если полагать, что все классы, представленные как фактические родовые параметры, являются потомками NUMERIC:

VECTOR [NUMERIC]

VECTOR [REAL]

VECTOR [COMPLEX]

Класс EMPLOYEE не порожден от NUMERIC, так что попытка использовать VECTOR [EMPLOYEE] приведет к ошибке времени компиляции.

Абстрактный характер NUMERIC не вызывает никаких проблем. Фактический параметр при порождении может быть как эффективным (примеры выше), так и отложенным (VECTOR [NUMERIC_COMPARABLE]), если он порожден от NUMERIC.

Аналогично описываются класс словаря и класс, поддерживающий сортировку:

class DICTIONARY [G, H -> HASHABLE] ...

class SORTABLE [G -> COMPARABLE] ...

Игра в рекурсию

Вот некий трюк с нашим примером: спросим себя, возможен ли вектор векторов? Допустим ли тип VECTOR [VECTOR [INTEGER]]?

Ответ следует из предыдущих правил: только если фактический родовой параметр совместим с NUMERIC. Сделать это просто - породить класс VECTOR от класса NUMERIC (см. упражнение 16.2):

indexing

description: "Векторы, допускающие сложение"

class

VECTOR [G -> NUMERIC]

inherit

NUMERIC

... Остальное - как и раньше...

Векторы, подобные этому, можно и впрямь считать "числовыми". Операции сложение и умножение дают структуру кольца, в котором роль нуля (zero) играет вектор из G– нулей, и роль единицы (unity) - вектор из G– единиц. Операция сложения в этом кольце - это, строго говоря, векторный вариант infix "+", речь о котором шла выше.

Можно пойти дальше и использовать VECTOR [VECTOR [VECTOR [INTEGER]]] и так далее - приятное рекурсивное приложение ограниченной универсальности.

И снова неограниченная универсальность

Конечно же, не все случаи универсальности ограничены. Форма - STACK [G] или ARRAY [G]– по-прежнему существует и называется неограниченной универсальностью. Пример DICTIONARY [G, H -> HASHABLE] показывает, что класс одновременно может иметь как ограниченные, так и неограниченные родовые параметры.

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