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

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

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

Возможно простое решение, основанное на предыдущем обсуждении и попытке присваивания. Рассмотрим универсальную функцию fitted (согласовать):


>fitted (other: GENERAL): like other is

>-- Текущий объект (Current), если его тип соответствует типу объекта,

>-- присоединенного к other, иначе void.

>do

>if other /= Void and then conforms_to (other) then

>Result ?= Current

>end

>end



Функция fitted возвращает текущий объект, но известный как сущность типа, присоединенного к аргументу. Если тип текущего объекта не соответствует типу объекта, присоединенного к аргументу, то возвращается Void. Обратите внимание на роль попытки присваивания. Функция использует компонент conforms_to из класса GENERAL, выясняющий совместимость типов пары объектов.

Замена conforms_to на другой компонент GENERAL с именем same_type дает нам функцию perfect_fitted (полное соответствие), которая возвращает Void, если типы обоих объектов не идентичны.

Функция fitted - дает нам простое решение проблемы соответствия лыжников без нарушения правил описания типов. Так, в код класса SKIER мы можем ввести новую процедуру и использовать ее вместо share, (последнюю можно сделать скрытой процедурой).


>safe_share (other: SKIER) is

>-- Выбрать, если допустимо, other как соседа по номеру.

>-- gender_ascertained - установленный пол

>local

>gender_ascertained_other: like Current

>do

>gender_ascertained_other := other .fitted (Current)

>if gender_ascertained_other /= Void then

>share (gender_ascertained_other)

>else

>"Вывод: совместное размещение с other невозможно"

>end

>end



Для other произвольного типа SKIER (а не только like Current) определим версию gender_ascertained_other, имеющую тип, закрепленный за Current. Гарантировать идентичность типов нам поможет функция perfect_ fitted.

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


>occupant1, occupant2: LIST [SKIER]



можно организовать цикл, выполняя на каждом шаге вызов:


>occupant1.item.safe_share (occupant2.item)



сопоставляющий элементы списков, если и только если их типы полностью совместимы.

Ключевые концепции

[x]. Статическая типизация - залог надежности, читабельности и эффективности.

[x]. Чтобы быть реалистичной, статической типизации требуется совместное применение механизмов: утверждений, множественного наследования, попытки присваивания, ограниченной и неограниченной универсальности, закрепленных объявлений. Система типов не должна допускать ловушек (приведений типа).

[x]. Практические правила повторного объявления должны допускать ковариантное переопределение. Типы результатов и аргументов при переопределении должны быть совместимыми с исходными.

[x]. Ковариантность, также как и возможность скрытия потомком компонента, экспортированного предком, в сочетании с полиморфизмом порождают редко встречающуюся, но весьма серьезную проблему нарушения типов.

[x]. Этих нарушений можно избежать, используя: глобальный анализ (что непрактично), ограничивая ковариантность закрепленными типами (что противоречит принципу "Открыт-Закрыт"), решение Кэтколл, препятствующее вызову полиморфной целью подпрограммы с ковариантностью или скрытием потомком.

Библиографические замечания

Ряд материалов этой лекции представлен в докладах на форумах OOPSLA 95 и TOOLS PACIFIC 95, а также опубликован в [M 1996a]. Ряд обзорных материалов заимствован из статьи [M 1989e].

Понятие автоматического выведения типов введено в [Milner 1989], где описан алгоритм выведения типов функционального языка ML. Связь между полиморфизмом и проверкой типов была исследована в работе [Cardelli 1984a].

Приемы повышения эффективности кода динамически типизированных языков в контексте языка Self можно найти в [Ungar 1992].

Теоретическую статью, посвященную типам в языках программирования и оказавшую большое влияние на специалистов, написали Лука Карделли (Luca Cardelli) и Петер Вегнер (Peter Wegner) [Cardelli 1985]. Эта работа, построенная на базе лямбда-исчисления (см. [M 1990]), послужила основой многих дальнейших изысканий. Ей предшествовала другая фундаментальная статья Карделли [Cardelli 1984].

Руководство по ISE включает введение в проблемы совместного применения полиморфизма, ковариантности и скрытия потомком [M 1988a]. Отсутствие надлежащего анализа в первом издании этой книги послужило причиной ряда критических дискуссий (первыми из которых стали комментарии Филиппа Элинка (Philippe Elinck) в бакалаврской работе "De la Conception-Programmation par Objets", Memoire de licence, Universite Libre de Bruxelles (Belgium), 1988), высказанных в работах [Cook 1989] и [America 1989a]. В статье Кука приведены несколько примеров, связанных с проблемой ковариантности, и предпринята попытка ее решения. Решение на основе типовых параметров для ковариантных сущностей на TOOLS EUROPE 1992 предложил Франц Вебер [Weber 1992]. Точные определения понятий системной корректности, а также классовой корректности, даны в [M 1992], там же предложено решение с применением полного анализа системы. Решение Кэтколл впервые предложено в [M 1996a]; см. также [M-Web].