На этом шаге рассмотрим пример множественного наследования.
В предыдущем примере продемонстрировано одиночное наследование: подкласс ПЛАН ВЫРАЩИВАНИЯ_ФРУКТОВ (FruitGrowingPlan) имел только один суперкласс ПЛАН ВЫРАЩИВАНИЯ_РАСТЕНИЙ (GrowingPlan). Иногда полезно реализовать наследование от нескольких суперклассов.
Предположим, например, что требуется определить класс, представляющий разновидности растений. Анализ предметной области показывает, что цветы, фрукты и овощи имеют свои особые свойства, существенные для технологии их выращивания. Например, для абстракции цветов важно знать периоды цветения и созревания семян. Аналогично, для абстракций фруктов и овощей важен момент сбора урожая. Исходя из этих соображений, можно создать два новых класса — ЦВЕТОК (Flower) и ПЛОДОНОСЯЩЕЕ_РАСТЕНИЕ(FruitVegetable) — наследники суперкласса РАСТЕНИЕ (Plant). Какую же модель следует выбрать, если растение и цветет, и плодоносит? Например, флористы часто используют для составления букетов цветки яблони, вишни и сливы. Для этой абстракции придется создать третий класс ЦВЕТУЩЕЕ_ПЛОДОНОСЯЩЕЕ_РАСТЕНИЕ (FlowerFruitVegetable) — наследник классов ЦВЕТОК (Flower) и ПЛАН ВЫРАЩИВНИЯ_ПЛОДОНОСЯЩИХ_РАСТЕНИЙ (FruitVegetablePlant).
Чтобы наилучшим образом выразить новую абстракцию и не допустить ошибку, следует создать классы, независимо друг от друга описывающие уникальные свойства цветов, а также фруктов и овощей соответственно. Эти два класса не имеют суперкласса. Они называются классами-примесями, поскольку смешиваются вместе с другими классами, создавая новые подклассы. Например, можно описать класс РОЗА (Rose) рис. 1, наследующий свойства классов РАСТЕНИЯ (Plant) и ЦВЕТОК (Flower). Таким образом, экземпляры подкласса РОЗА (Rose) наследуют структуру и поведение классов РАСТЕНИЕ (Plant) и ЦВЕТОК (Flower).
Рис.1. Класс Rose — наследник
нескольких суперклассов
Точно так же можно определить класс МОРКОВЬ (Carrot), продемонстрированный на рис. 2.
Рис.2. Класс Carrot —
наследник нескольких суперклассов
В обоих случаях подкласс образуется путем наследования свойств двух суперклассов. Определим теперь класс, описывающий свойства вишни, которая цветет и плодоносит (рис. 3).
Рис.3. Класс Carrot —
наследник нескольких суперклассов
Несмотря на то что множественное наследование представляет собой довольно простую концепцию, оно создает некоторые сложности для языков программирования — конфликты имен между различными суперклассами и повторное наследование. Конфликты имен возникают, когда в двух или большем числе суперклассов определены поля или операции с одинаковыми именами. Повторное наследование возникает, когда несколько суперклассов наследуют свойства общего суперкласса. В таких ситуациях возникает ромбическая структура наследования, и проектировщик должен решить, сколько копий полей должен унаследовать класс самого нижнего уровня у суперкласса самого верхнего уровня — одну или две (рис. 4).
Рис.4. Повторное наследование
В некоторых языках повторное наследование запрещено, в других конфликт разрешается однозначно, а в языке С++ решение должен принять сам программист. В языке C++ для запрещения дублирования повторяющихся структур используются виртуальные базовые классы, иначе в подклассе могут появиться дубликаты (для которых потребуется явное указание происхождения каждой).
Множественным наследованием часто злоупотребляют. Например, сладкая вата — это разновидность сладости, но никак не ваты. К этой ситуации можно применить описанный ранее критерий: если класс В не является разновидностью класса А, то он не должен быть наследником класса А. Часто плохо продуманные структуры множественного наследования следует свести к одному суперклассу, по возможности используя агрегацию других классов подклассами.
На следующем шаге рассмотрим пример агрегации.