Пусть, согласно произведенной классификации, объекты "скрипач с фамилией Петров" и "водитель автомобиля Сидоров" будут экземплярами разных классов. Для того чтобы получить объект "Иванов, являющийся одновременно скрипачом и водителем", необходим особый класс, который может быть получен из классов "скрипач" и "водитель автомобиля" множественным наследованием (рис. 8.3). Теперь работодатель, послав особое сообщение делегирования, может поручить (делегировать) объекту "Иванов" выполнять функцию либо водителя, либо скрипача. Объект "Иванов", находящийся за рулем автомобиля, не должен начать играть на скрипке. Для этого должен быть реализован механизм самоделегирования полномочий — объект "Иванов", находясь за рулем, запрещает сам себе игру на скрипке. Таким образом, понятие обязанности или ответственности за выполнение действия является фундаментальным в объектно-ориентированном программировании.
Рис. 8.3. Пример простого и множественного наследования
В системах программирования с отсутствующим множественным наследованием задачи, требующие множественного наследования, всегда могут быть решены композицией (агрегированием) с последующим делегированием полномочий.
Композиция объектов — это реализация составного объекта, состоящего из нескольких совместно работающих объектов и образующих единое целое с новой, более сложной функциональностью.
Агрегированный объект — объект, составленный из подобъектов. Подобъекты называются частями агрегата, и агрегат отвечает за них. Например, в системах с множественным наследованием шахматная фигура ферзь может быть унаследована от слона и ладьи. В системах с отсутствующим множественным наследованием можно получить ферзя двумя способами. Согласно первому способу, можно создать класс "любая_фигура" и далее, в периоде выполнения, делегировать полномочия каждому объекту-экземпляру данного класса быть ладьей, слоном, ферзей, пешкой и т. д. По второму способу после получения классов "ладья" и "слон" их можно объединить композицией в класс "ферзь". Теперь объект класса "ферзь" можно использовать как объект "ферзь" или даже как объект "слон", для чего объекту "ферзь" делегируется выполнение полномочий слона. Более того, можно делегировать объекту "ферзь" полномочия стать объектами "король" или даже "пешка"! Для композиции требуется, чтобы объединяемые объекты имели четко определенные интерфейсы. И у наследования, и у композиции есть достоинства и недостатки.
Наследование класса определяется статически на этапе компиляции; его проще использовать, поскольку оно напрямую поддержано языком программирования.
Но у наследования класса есть и минусы. Во-первых, нельзя изменить унаследованную от родителя реализацию во время выполнения программы, поскольку само наследование фиксировано на этапе компиляции. Во-вторых, родительский класс нередко, хотя бы частично, определяет физическое представление своих подклассов. Поскольку подклассу доступны детали реализации родительского класса, то часто говорят, что наследование нарушает инкапсуляцию. Реализации подкласса и родительского класса настолько тесно связаны, что любые изменения последней требуют изменять и реализацию подкласса.
Композиция объектов определяется динамически во время выполнения за счет того, что объекты получают ссылки на другие объекты. Композицию можно применить, если объекты соблюдают интерфейсы друг друга. Для этого, в свою очередь, требуется тщательно проектировать интерфейсы, так чтобы один объект можно было использовать вместе с широким спектром других. Но и выигрыш велик, поскольку доступ к объектам осуществляется только через их интерфейсы, мы не нарушаем инкапсуляцию. Во время выполнения программы любой объект можно заменить другим, лишь бы он имел тот же тип. Более того, поскольку при реализации объекта кодируются прежде всего его интерфейсы, то зависимость от реализации резко снижается.
Композиция объектов влияет на дизайн системы и еще в одном аспекте. Отдавая предпочтение композиции объектов, а не наследованию классов, вы инкапсулируете каждый класс и даете ему возможность выполнять только свою задачу. Классы и их иерархии остаются небольшими, и вероятность их разрастания до неуправляемых размеров невелика. С другой стороны, дизайн, основанный на композиции, будет содержать больше объектов (хотя число классов, возможно, уменьшится), и поведение системы начнет зависеть от их взаимодействия, тогда как при другом подходе оно было бы определено в одном классе.
Читать дальше