<p>Слово в защиту реализаций</p>

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

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

<p>Два стиля</p>

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

Итак, есть два отношения - "быть потомком" и "быть клиентом"; две формы повторного использования - интерфейсов и реализаций; скрытие информации и его отсутствие; защита от изменений в поставляемых модулях и отсутствие таковой.

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

Повторное использование интерфейсов

Информация скрывается

Исходная реализация защищена

Повторное использование реализаций

Информация не скрывается

Исходная реализация не защищена

КлиентПотомок

Таблица 16.1.Слияние четырех противоположностей

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

<p>Выборочный экспорт</p>

Говоря о наследовании и скрытии информации, нельзя обойти вопрос о выборочном экспорте компонентов. Класс A, выборочно экспортирующий f классу B:

class A feature {B, ...}

f...

...

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

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

Согласно принципу Скрытия информации, а также принципу Открыт-Закрыт, разработчику A дано право решать, делать ли f доступным для B, однако, ему запрещено ограничивать свободу разработчика B. Тем самым, имеет место правило:

Правило наследования при выборочном экспорте

Выборочно экспортированный компонент доступен как самому классу, так и всем его потомкам.

<p>Ключевые концепции</p>

[x]. К инварианту класса автоматически добавляются инварианты его родителей.

[x]. В подходе Проектирования по Контракту наследование, переопределение и динамическое связывание приводят к идее субподрядов.

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

[x]. Повторное объявление утверждений может использовать только require else (при объединении с предусловием связкой "или") и ensure then (при объединении с постусловием связкой "и"). Применение require/ensure запрещено. В отсутствие названных предложений подпрограмма сохраняет исходные утверждения.

[x]. Универсальный класс GENERAL и допускающий настройку его наследник обеспечивают переопределяемые компоненты, представляющие общий интерес для всех создаваемых разработчиком классов. Класс NONE замыкает решетку наследования снизу.

[x]. Заморозив компонент, можно гарантировать его вечную семантическую уникальность.

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

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

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

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

Перейти на страницу:

Похожие книги