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

Набор полезных возможностей предоставляет класс TEST, инкапсулирующий ряд механизмов тестирования класса: прием и хранение данных от пользователя, вывод и хранение результата, сравнение, регрессное тестирование и т.д. Хотя решение с использованием вложения может быть предпочтительным, неплохо иметь возможность при тестировании класса X определять класс X_TEST, порожденный от X и TEST.

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

<p>Лунка и кнопка</p>

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

Среда разработки ISE, описанная в лекции 19 курса "Основы объектно-ориентированного проектирования", подобно другим графическим приложениям, содержит "кнопки" для выполнения определенных действий. В среду встроен механизм "выбрать и перетащить" (pick and throw), аналог традиционного механизма буксировки drag-and-drop. С его помощью можно выбрать объект на экране; при этом курсор мыши превращается в "камешек", форма которого указывает тип выбранного объекта. Камешек можно перетащить и опустить в лунку, форма которой соответствует камешку, инициируя тем самым определенное действие. Например, инструментарий Class Tool, позволяющий исследовать свойства класса, имеет "классную лунку", опустив в которую камешек нового класса, вы перенастроите инструмент на показ его свойств.

Рис. 15.12.  Pick and throw (Выбрать и перетащить)

Обратите внимание на нижнюю строку с кнопками форматирования. Нажатие каждой из них позволяет получить разнообразную информацию о классе ARRAY, например краткую форму класса. Как показано на рисунке, пользователь, работая в окне Feature Tool, выбрал щелчком правой кнопки класс INTEGER. Он передвигает его в направлении "лунки" класса в окне Class Tool, настроенного сейчас на ARRAY. Перетаскивание завершается щелчком правой кнопки на "лунке" класса, форма которой соответствует форме камешка. Тем самым Class Tool будет перенастроен на работу с выбранным классом INTEGER.

Иногда удобнее, чтобы "лунка" была одновременно и кнопкой, что позволяет не только "загонять" в нее объект, но независимо от этого щелкать по ней левой кнопкой. Таковой является наша "лунка" класса, точка внутри которой указывает на присутствие в ней объекта (сначала ARRAY, а затем INTEGER). Щелчок по ней левой кнопкой перенастроит инструмент на работу с текущим объектом, что полезно, когда дисплей отражает другую информацию. Такая лунка с кнопкой реализуется специальным классом BUTTONHOLE.

Нетрудно догадаться, что класс BUTTONHOLE возникает в результате наследования от классов BUTTON и HOLE. Новый класс сочетает в себе компоненты и свойства обоих родителей, реагирует как кнопка, и допускает операции как над лункой.

<p>Оценка</p>

Приведенные примеры наглядно проиллюстрировали мощь и силу механизма множественного наследования. Необходимость его применения подтверждена опытом построения универсальных библиотек [M 1994a].

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

<p>Переименование компонентов</p>

Иногда при множественном наследовании возникает проблема конфликта имен (name clash). Ее решение - переименование компонентов (feature renaming) - не только снимает саму проблему, но и способствует лучшему пониманию природы классов.

<p>Конфликт имен</p>

Каждый класс обладает доступом ко всем компонентам своих родителей. Он может использовать их, не указывая тот класс, в котором они были описаны. После обработки inherit в классе class C inherit A ... метод f класса C становится известен как f. То же справедливо и для клиентов: при объявлении сущности x типа C вызов компонента записывается как x.f без каких-либо ссылок на A. Все метафоры "хромают", иначе можно было бы говорить, что наследование - форма усыновления: C усыновляет все компоненты A.

Усыновление не меняет присвоенных имен, и набор имен компонентов данного класса содержит наборы имен компонентов каждого его родителя.

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

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