Выбрать главу

class B inherit

A

export {X, Y, Z} f end

-- Сделать f доступным определенным классам

...

Применение

Характерным примером является создание нескольких вариантов одной абстракции.

Представим себе GENERAL_ACCOUNT - класс, содержащий все необходимые операции для работы с банковскими счетами: процедуры open, withdraw, deposit, code (для снятия денег через банкомат), change_code и т.д.,- но не предназначенный для использования клиентами напрямую, а потому не экспортирующий никаких подпрограмм. Его потомки выступают как разные облики родителя: они не содержат новых компонентов и отличаются лишь предложениями экспорта. Один экспортирует open и deposit, второй, наряду с ними, - withdraw и code, и т. д.

Рис. 16.12.  Разные облики одной абстракции

Эта схема в обсуждении методологии наследования (см. лекцию 6 курса "Основы объектно-ориентированного проектирования") носит название наследования функциональных возможностей (facility inheritance).

Понятие облика (view) является классическим в области баз данных, где необходимо дифференцировать пользователей, работающих с данными, предоставляя им разные права.

Другой пример касается классов, введенных, когда речь шла о множественном наследовании. Компонент right класса CELL скрыт в нем или, точнее говоря, экспортируется лишь классу LIST. Фактически так обстоят дела со всеми компонентами CELL, поскольку этот класс был изначально нацелен на работу со списками. Однако в дереве (классе TREE), потомке как CELL, так и LIST, right теперь означает доступ к правому брату и является респектабельным членом общества экспортируемых компонентов.

Зачем нужна такая гибкость?

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

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

[x]. мне не известно о публикации рекомендаций по применению этой возможности, неясно, когда компонент должен передаваться потомкам, а когда быть скрытым. Конструкции языка, за которыми нет ни малейшей теории, имеют весьма сомнительную ценность. (Для сравнения: правило, посвященное методологии скрытия информации, совершенно прозрачно: то, что принадлежит АТД, и надлежит экспортировать; прочее следует скрыть.)

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

При близком рассмотрении отсутствие ясных методологических установок не удивляет. Наследование является воплощением принципа Открыт-Закрыт: позволяя выбрать готовый класс, написанный вами или другим программистом вчера или 20 лет назад, и обнаружить, что с ним можно делать нечто полезное, что даже не предполагалось при его проектировании.

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

Пример классов CELL и TREE характерен: при разработке CELL его целью была лишь поддержка работоспособности LIST, а потому right и put_right служили в нем исключительно внутренним целям. И лишь позднее этим компонентам нашли новое применение в классе-потомке TREE. Не будь этой открытости, наследование почти полностью утратило бы свой шарм.

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

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

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

Знакомясь с объектным подходом по другим источникам, вы могли видеть в них предостережения использования "наследования реализаций". Однако в нем нет ничего плохого.

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

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

Эти формы повторного использования взаимно дополняют друг друга и обе совершенно законны.

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

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

Слово в защиту реализаций

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

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