Визуальное программирование и MFC

       

Агрегирование


Если включение так легко реализовать, почему бы не использовать эту технику для повторного применения объектов СОМ всегда? Повторное применение объекта всегда может быть реализовано посредством включения — как правило, этого достаточно. Как правило, но не всегда. Точнее, включение — не всегда самое эффективное решение.

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

Эту проблему устраняет агрегирование (aggregation). Оно позволяет внешнему объекту представлять в качестве собственных интерфейсы, на самом деле реализованные внутренним объектом . Когда клиент запрашивает у внешнего объекта указатель на подобный интерфейс, этот объект возвращает указатель на интерфейс внутреннего, агрегированного объекта. (Методы внутреннего объекта добавляются, или агрегируются, к методам внешнего объекта.) Клиент ничего об этом не знает: возвращенный интерфейс обеспечивается для него только одним известным ему объектом, а именно внешним. Агрегирование повышает эффективность, но, как и включение, абсолютно невидимо для клиента.

Однако агрегирование не невидимо для участвующих в нем объектов. В отличие от включения агрегирование требует поддержки со стороны внутреннего объекта. Для этой цели он должен быть особым образом написан; в противном случае объект можно повторно использовать только путем включения. Что же такого особенного в агрегировании, что требует поддержки со стороны внутреннего объекта? Проблемы вытекают из операций, поддержка которых обязательна для всех объектов — операций, определенных в IUnknown. Два основных вопроса при реализации агрегирования — это обеспечение правильного подсчета ссылок и корректной работы Ouerylnterface.




Чтобы понять причины этих проблем, обратимся снова к рисинку. Внешний объект предоставляет интерфейс А и, конечно, поддерживает IUnknown. Внутренний, агрегируемый объект поддерживает интерфейсы В иIUnknown. Так как внешний объект агрегирует внутренний, а не просто включает его, то интерфейс В доступен клиенту внешнего объекта непосредственно.

Допустим, у клиента есть указатель интерфейса В. С точки зрения клиента, этот интерфейс предоставляется ему тем же объектом, что и интерфейс А. Так что у клиента должна быть возможность получить указатель интерфейса А вызовом Ouerylnterface через указатель на интерфейс В. Но откуда внутренний объект знает, что внешний поддерживает интерфейс А? И если клиент вызывает IUnknown: :AddRef через указатель на интерфейс В, то как об этом узнает внешний объект? В конце концов, с точки зрения клиента, существует лишь один объект, так что каждый из этих вызовов должен быть успешным.

Решение обеих проблем очевидно. Любой внутренний объект должен делегировать вызовы методов своего IUnknown методам IUnknown внешнего объекта (агрегирующего его). Следовательно, внутреннему объекту нужно как-то передать указатель на интерфейс IUnknown внешнего. Данный указатель, известный под несколько загадочным названием управляющий IUnknown (controlling unknown), передается как параметр либо CoCreateInstance, либо IClassFactory::CreateInstance при создании агрегируемого объекта. Если соответствующий параметр NULL (самый распространенный случай), то объект знает, что он не агрегируется, и будет обрабатывать все вызовы методов IUnknown самостоятельно. Если не NULL, новый объект будет функционировать только как агрегированный внутренний объект некоторого внешнего объекта — того, что передал ему свой управляющий IUnknown. В последнем случае вызовы методов IUnknown внутреннего объекта делегируются методам IUnknown внешнего объекта, т.е. управляющему IUnknown.

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


Содержание раздела