Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Роджерсон Д. - Основы COM - 2000.pdf
Скачиваний:
412
Добавлен:
13.08.2013
Размер:
2.4 Mб
Скачать

131

return CLASS_E_CLASSNOTAVAILABLE;

}

 

// Создать фабрику класса

// В конструкторе нет AddRef

CFactory* pFactory = new CFactory;

if (pFactory == NULL)

 

{

 

return E_OUTOFMEMORY;

 

}

 

// Получить запрошенный интерфейс

HRESULT hr = pFactory->QueryInterface(iid, ppv); pFactory->Release();

return hr;

}

//

// Регистрация сервера

//

STDAPI DllRegisterServer()

{

return RegisterServer(g_hModule, CLSID_Component2, g_szFriendlyName, g_szVerIndProgID, g_szProgID);

}

STDAPI DllUnregisterServer()

{

return UnregisterServer(CLSID_Component2, g_szVerIndProgID, g_szProgID);

}

///////////////////////////////////////////////////////////

//

// Информация о модуле DLL

//

BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)

{

if (dwReason == DLL_PROCESS_ATTACH)

{

g_hModule = hModule;

}

return TRUE;

}

Листинг 8-4 Реализация внутреннего (агрегируемого) компонента

Слепое агрегирование

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

А что, если внешний компонент хочет агрегировать несколько интерфейсов внутреннего? Внешний компонент легко модифицировать так, чтобы поддерживать еще один интерфейс внутреннего компонента:

else is ((iid == IID_IY) || (iid == IID_IZ))

{

return m_pUnknownInner->QueryInterface(iid, ppv);

}

Конечно, для изменения кода внешнего компонента нам потребуется доступ к этому коду и компилятор. Но как быть, если мы хотим, чтобы клиент имел доступ ко всем интерфейсам внутреннего компонента, включая то, что будут добавлены после того, как внешний компонент будет написан, скомпилирован и поставлен заказчику? Все

132

очень просто — удалите условие. Вместо проверки идентификатора интерфейса можно просто слепо передавать его внутреннему компоненту:

...

else if (iid == IID_IX)

{

*ppv = static_cast<IX*>(this);

}

else // Нет условия

{

return m_pUnknownInner->QueryInterface(iid, ppv);

}

...

Данная процедура называется слепым агрегированием (blind aggregation), так как внешний компонент слепо передает идентификаторы интерфейсов внутреннему. При слепом агрегировании внешний компонент не контролирует, какие из интерфейсов внутреннего компонента он предоставляет клиенту. В большинстве случаев лучше не прибегать к слепому агрегированию. Одна из причин этого в том, что внутренний компонент может поддерживать интерфейсы, не совместимые с интерфейсами, которые поддерживаются внешним компонентом. Например, внешний компонент для сохранения файлом может поддерживать интерфейс ISlowFile, тогда как внутренний для этой же цели поддерживает интерфейс IFastFile. Предположим, что клиент всегда запрашивает IFastFile прежде, чем запрашивать ISlowFile. Если внешний компонент слепо агрегирует внутренний, клиент получит указатель на IFastFile внутреннего компонента. Внутренний компонент ничего не знает о внешнем и поэтому не будет правильно сохранять информацию, связанную с последним. Проще всего избежать таких конфликтов, не используя слепое агрегирование.

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

Метаинтерфейсы

Интерфейсы, вызывающие конфликты внутреннего и внешнего компонента, — это обычно интерфейсы с перекрывающейся функциональностью. Если Вы можете гарантировать, что функциональность интерфейсов не перекрывается, вероятность конфликтов уменьшается. Добиться этого непросто, так как внутренний компонент может быть модернизирован для поддержки новых интерфейсов, которых внешний компонент не ожидает. Вероятность конфликта с существующими интерфейсами компонента минимальна в случае метаинтерфейсов (metainterfaces), или интерфейсов класса. Метаинтерфейсы манипулируют самим компонентом, а не абстракцией, которую он представляет.

Предположим, что у нас есть программа преобразования растровых изображений. Пользователь может модифицировать растровое изображение с помощью различных алгоритмов. Последние реализованы как внутренний компонент, который пользователь может добавлять в систему во время работы. Каждый внутренний компонент может считывать и сохранять растровые образы, а также преобразовывать их в соответствии с некоторым особым алгоритмом. У внешнего компонента есть интерфейс ISetColors, устанавливающий цвета, с которыми работает внутренний компонент. Внешний компонент также имеет интерфейс IToolInfo, который отображает значки разных алгоритмов преобразования на панели инструментов и создает внутренний компонент, когда пользователь выбирает соответствующий значок (рис. 8-7).

ISetColors — это пример обычного интерфейса, который расширяет абстракцию алгоритма преобразования. Компонент преобразования, вероятно, уже поддерживает интерфейс, например, IColors, для манипуляции набором цветов. Второй интерфейс, IToolInfo — пример метаинтерфейса. Все его операции служат для того, чтобы предоставить приложению способ работать с алгоритмами преобразования как с инструментами. Он не имеет никакого отношения к собственно преобразованию растровых образов. Этот интерфейс не расширяет абстракцию компонента преобразования растровых образов, но предоставляет клиенту информацию о самом этом компоненте.

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

Соседние файлы в предмете Программирование на C++