Полагаясь на закрепление типов

Полагаясь на закрепление типов

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

При описании классов SKIER и SKIER1 вас не могло не посетить желание, воспользовавшись закрепленными объявлениями, избавиться от многих переопределений. Закрепление - это типичный ковариантный механизм. Вот как будет выглядеть наш пример (все изменения подчеркнуты):

class SKIER feature

roommate: like Current

share (other: like Current) is ... require ... do

roommate := other

end

...

end

class SKIER1 feature

accommodation: ROOM

accommodate (r: like accommodation) is ... require ... do

accommodation := r

end

end

Теперь потомки могут оставить класс SKIER без изменений, а в SKIER1 им понадобится переопределить только атрибут accommodation. Закрепленные сущности: атрибут roommate и аргументы подпрограмм share и accommodate - будут изменяться автоматически. Это значительно упрощает работу и подтверждает тот факт, что при отсутствии закрепления (или другого подобного механизма, например, типовых переменных) написать ОО-программный продукт с реалистичной типизацией невозможно.

Но удалось ли устранить нарушения корректности системы? Нет! Мы, как и раньше, можем перехитрить проверку типов, выполнив полиморфные присваивания, вызывающие нарушения системной корректности.

Правда, исходные варианты примеров будут отклонены. Пусть:

s: SKIER; b: BOY; g: GIRL

...

create b;create g;-- Создание объектов BOY и GIRL.

s := b; -- Полиморфное присваивание.

sl.share (g)

Аргумент g, передаваемый share, теперь неверен, так как здесь требуется объект типа like s, а класс GIRL не совместим с этим типом, поскольку по правилу закрепленных типов ни один тип не совместим с like s, кроме него самого.

Впрочем, радоваться нам не долго. В другую сторону это правило говорит о том, что like s совместим с типом s. А значит, используя полиморфизм не только объекта s, но и параметра g, мы можем снова обойти систему проверки типов:

s: SKIER; b: BOY; g: like s; actual_g: GIRL;

...

create b; create actual_g -- Создание объектов BOY и GIRL.

s := actual_g; g := s -- Через s присоединить g к GIRL.

s := b -- Полиморфное присваивание.

s.share (g)

В результате незаконный вызов проходит.

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

anchor s: SKIER

Разрешим объявления вида like s лишь тогда, когда s описано как anchor. Изменим правила совместимости так, чтобы гарантировать: s и элементы типа like s могут присоединяться (в присваиваниях или передаче аргумента) только друг к другу.

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

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

r (u: Y) ...

тогда как у класса C - родителя D это выглядело

r (u: X) ...

где Y соответствовало X, то теперь переопределение компонента r будет выглядеть так:

r (u: like your_anchor) ...

Остается только в классе D переопределить тип your_anchor.

Это решение проблемы ковариантности - полиморфизма будем называть подходом Закрепления (Anchoring). Более аккуратно следовало бы говорить: "Ковариация только через Закрепление". Свойства подхода привлекательны:

[x]. Закрепление основано на идее строгого разделения ковариантных и потенциально полиморфных (или, для краткости, полиморфных) элементов. Все сущности, объявленные как anchor или like some_anchor ковариантны; прочие-полиморфны. В каждой из двух категорий допустимы любые присоединения, но нет сущности или выражения, нарушающих границу. Нельзя, например, присвоить полиморфный источник ковариантной цели.

[x]. Это простое и элегантное решение нетрудно объяснить даже начинающим.

[x]. Оно полностью устраняет возможность нарушения системной корректности в ковариантно построенных системах.

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

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

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

Казалось бы, дискуссию можно на этом закончить. Так почему же подход Закрепления не полностью нас устраивает? Прежде всего, мы еще не касались проблемы скрытия потомком. Кроме этого, основной причиной продолжения дискуссии является проблема, уже высказанная при кратком упоминании типовых переменных. Раздел сфер влияния на полиморфную и ковариантную часть, чем-то похож на результат Ялтинской конференции. Он предполагает, что разработчик класса обладает незаурядной интуицией, что он в состоянии для каждой введенной им сущности, в частности для каждого аргумента раз и навсегда выбрать одну из двух возможностей:

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

[x]. Сущность является субъектом переопределения типов, то есть она либо закреплена, либо сама является опорным элементом.

Но как разработчик может все это предвидеть? Вся привлекательность ОО-метода во многом выраженная в принципе Открыт-Закрыт как раз и связана с возможностью изменений, которые мы вправе внести в ранее сделанную работу, а также с тем, что разработчик универсальных решений не должен обладать бесконечной мудростью, понимая, как его продукт смогут адаптировать к своим нуждам потомки.

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

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

[x]. Скрытие потомком защита от многих неудач при создании класса. Можно критиковать проект, в котором RECTANGLE, используя тот факт, что он является потомком POLYGON, пытается добавить вершину. Взамен можно было бы предложить структуру наследования, в которой фигуры с фиксированным числом вершин отделены от всех прочих, и проблемы не возникало бы. Однако при разработке структур наследования предпочтительнее всегда те, в которых нет таксономических исключений. Но можно ли их полностью устранить? Обсуждая ограничение экспорта в одной из следующих лекций, мы увидим, что подобное невозможно по двум причинам. Во-первых, это наличие конкурирующих критериев классификации. Во-вторых, вероятность того, что разработчик не найдет идеального решения, даже если оно существует.

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

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

1.4.1. Задание на закрепление материала

Из книги автора

1.4.1. Задание на закрепление материала Задание 1.1. Начертите схему электрической цепи из последовательно соединенных резистора, катушки индуктивности и конденсатора, изображенную на рис. 1.21. Сохраните свой чертеж в папке Projects под именем RLC_MIX1.sch. Рис. 1.21. Схема цепи из


3.1.1. Задания на закрепление материала

Из книги автора

3.1.1. Задания на закрепление материала Задание 3.1. Подумайте, к чему относится угол фазы конденсатора -68.3°, установленный программой PSPICE: к общему напряжению или, как это часто бывает в электротехнике, к току в цепи последовательного включения конденсатора и


4.4.1. Задания на закрепление материала

Из книги автора

4.4.1. Задания на закрепление материала Задание 4.1. Создайте диаграмму входного и выходного напряжения для электросхемы RC_TRANS.sch в сокращенном временном интервале от 0 с до 1 мс. Задание 4.2. Уменьшите ширину шага вычислений (поле Step Ceiling) для моделирования электросхемы RC_TRANS.sch


5.3.1. Задания на закрепление материала

Из книги автора

5.3.1. Задания на закрепление материала Задание 5.1. Поэкспериментируйте с различными вариантами линейного и логарифмического форматирования координатных осей диаграммы частотной характеристики электросхемы последовательного включения с резистивно-емкостной связью.


6.6.3. Задание на закрепление материала

Из книги автора

6.6.3. Задание на закрепление материала Задание 6.4. Загрузите на экран редактора SCHEMATICS схему последовательной цепи RLC.sch (рис. 6.1) и проведите для нее анализ AC Sweep в диапазоне частот 100 Гц–1 МГц. По окончании моделирования выведите на экран PROBE диаграмму частотных характеристик


7.5.1. Задание на закрепление материала

Из книги автора

7.5.1. Задание на закрепление материала Задание 7.1. Проведите для каждой из схем U_I.sch (Ri=1 кОм и Uq=10 В) и I_U.sch (Rp=1 кОм и Iq=10 мА) анализ DC Sweep, при котором в качестве дополнительной изменяемой переменной будет служить значение сопротивления нагрузки RH (от RH=1 кОм до RH=10 кОм), и тем


8.5.1. Задание на закрепление материала

Из книги автора

8.5.1. Задание на закрепление материала Задание 8.1. Проведите для схемы фильтра нижних частот с крутизной фронта 12 дБ, изображенной на рис. 8.22, анализ Transient Analysis + Parametric Sweep, чтобы исследовать ее переходную характеристику при различных уровнях импеданса.Для этого выполните в


R.3.6.3 Имена типов

Из книги автора

R.3.6.3 Имена типов Основные и производные типы можно поименовать с помощью механизма typedef (§R.7.1.3), а семейство типов и функций можно задать и поименовать с помощью механизма шаблона типов


R.8.1 Имена типов

Из книги автора

R.8.1 Имена типов Имя типа необходимо указывать при задании операции явного преобразования типа или в качестве параметра в операциях sizeof или new. Для этого служит конструкция имя-типа, которая синтаксически эквивалентна описанию объекта или функции этого типа, в котором


Метаданные типов

Из книги автора

Метаданные типов Возможность полного описания типов (классов, интерфейсов, структур, перечней и делегатов) с помощью метаданных является главной особенностью платформы .NET. Многие .NET-технологии, такие как сериализация объектов, удаленное взаимодействие .NET и Web-сервисы XML,


Закрепление областей

Из книги автора

Закрепление областей Предположим, что вы работаете с большой таблицей. В процессе работы вам приходится часто использовать полосы прокрутки, чтобы ввести данные в различные ячейки. При этом заголовки столбцов (самые различные, например: Товар, Цена, Количество,


8.7 Имена Типов

Из книги автора

8.7 Имена Типов Иногда (для неявного задания преобразования типов и в качестве параметра sizeof или new) нужно использовать имя тпа данных. Это выполняется при помощи «имени типа» которое по сути является описанием для объекта этого типа, в котором опущено имя объекта.имя_типа:


Переключатели типов

Из книги автора

Переключатели типов Как и раньше, отобразить мастер для выполнения поиска можно с помощью сочетания клавиш Windows+F (рис. 3.2). Обратите внимание на панель Показать только данного мастера. С ее помощью можно выполнить поиск среди файлов определенного расширения. Данная панель


Однократные функции, закрепление и универсальность

Из книги автора

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