Снова об интерфейсе и реализации
В предыдущей главе интерфейс СОМ был определен как абстрактный набор
операций, выражающий некоторую функциональность, которую может экспортировать
объект. Интерфейсы СОМ описаны на языке IDL (Interface Definition
Language - язык определений интерфейса) и имеют логические имена, которые
указывают на моделируемые ими функциональные возможности. Ниже приведено
IDL-определение СОМ-интерфейса IApe:
[object, uuid(753A8A7C-A7FF-11d0-8C30-0080C73925BA)]
interface IApe : Unknown {
import "unknwn.idl";
HRESULT EatBanana(void);
HRESULT SwingFromTree(void);
[propget] HRESULT Weight([out, retval] long *plbs);
}
Сопровождающая IApe документация должна специфицировать примерную
семантику трех операций: EatBanana, SwingFromTree и
Weight. Все объекты, раскрывающие IАре посредством QueryInterface,
должны гарантировать, что их реализации этих методов придерживаются семантического
контракта IАре. В то же время определения интерфейса почти всегда
специально оставляют место для интерпретации разработчиком объекта. Это
означает, что клиенты никогда не могут быть полностью уверены в точном
поведении любого заданного метода, а только в том, что его поведение будет
следовать схематическим правилам, описанным в документации к интерфейсу.
Эта контролируемая степень неопределенности является фундаментальной характеристикой
полиморфизма и одной из основ развития объектно-ориентированного программного
обеспечения.
Рассмотрим только что приведенный интерфейс IАре. Вероятно
(и даже возможно), что будет более одной реализации интерфейса IАре.
Поскольку определение IАре является общим для всех реализаций,
то предположения, которые могут сделать клиенты о поведении метода EatBanana,
должны быть достаточно неопределенными, чтобы позволить каждой обезьяне
- гориллам, шимпанзе и орангутангам (все они могут реализовывать интерфейс
IАре), получить свои допустимые (но слегка различные) интерпретации
данной операции. Без этой гибкости полиморфизм невозможен.
СОМ определенно трактует интерфейсы, реализации и классы как три различных
понятия. Интерфейсы являются абстрактными протоколами для связи с объектом.
Реализации - это конкретные типы данных, поддерживающие один или несколько
интерфейсов с помощью точных семантических интерпретаций каждой из абстрактных
операций интерфейса. Классы - это именованные реализации, представляющие
собой конкретные типы, которым можно приписывать значения, и формально
называются СОМ-классами, или коклассами (coclasses).
В смысле инкапсуляции о СОМ-классе известно только его имя и потенциальный
список интерфейсов, которые он выставляет. Подобно СОМ-интерфейсам, СОМ-классы
именуются с использованием GUID (globally unique identifier -
глобально уникальный идентификатор), хотя если GUID используются
для именования СОМ-классов, то они называются идентификаторами класса
- CLSID. Аналогично именам интерфейсов, эти имена классов должны
быть хорошо известны клиенту до того, как он их использует. Поскольку
для обеспечения полиморфизма СОМ-интерфейсы являются семантически неопределенными,
то СОМ не позволяет клиентам просто запрашивать любую доступную
реализацию данного интерфейса. Вместо этого клиенты должны точно специфицировать
требуемую реализацию. Это лишний раз подчеркивает тот факт, что СОМ-интерфейсы
- это всего лишь абстрактные коммуникационные протоколы, единственное
назначение которых - обеспечить клиентам связь с объектами, принадлежащими
конкретным, имеющим ясную цель классам реализации 1.
Кроме того, что реализации могут быть именованы с помощью CLSID,
СОМ поддерживает текстовые псевдонимы, так называемые программные идентификаторы
(programmatic identifiers), иначе ProgID. Эти ProgID
поступают в формате libraryname.classname.version и, в отличие
от CLSID, являются уникальными только по соглашению. Клиенты могут преобразовывать
ProgID в CLSID и обратно с помощью API-функций СОМ CLSIDFromProgID
и ProgIDFromCLSID:
HRESULT CLSIDFromProgID([in, string] const OLECHAR *pwszProgID, [out] CLSID *pclsid);
HRESULT ProgIDFromCLSID([in] REFCLSID rclsid, [out, string] OLECHAR **ppwszProgID);
Для преобразования ProgID в CLSID нужно просто вызвать
CLSIDFromProgID:
HRESULT GetGorillaCLSID(CLSID& rclsid)
{
const OLECHAR wszProgID[] = OLESTR("Apes.Gorilla.1");
return CLSIDFromProgID(wszProgID, &rclsid);
}
На этапе выполнения будет просматриваться база данных конфигураций СОМ
для преобразования ProgID Apes.Gorilla.1 в CLSID, соответствующий
классу реализации СОМ.
1 Хотя и мало смысла запрашивать "любую
доступную реализацию" данного интерфейса, иногда имеет смысл произвести
семантическое группирование реализаций, имеющих определенные общие черты
высокого уровня, например, чтобы все они были животными или чтобы все
они имели службу регистрации. Чтобы обеспечить обнаружение этого типа
компонентов, СОМ поддерживает объявление такой систематики (taxonomy)
посредством использования категорий компонентов (component categories).
Поскольку часто это тот случай, когда все классы, принадлежащие к одной
категории компонентов, будут реализовывать одно и то же множество интерфейсов,
то такое условие, без сомнения, является достаточным для принадлежности
к одной категории компонентов.
Объекты классов
Активация
Использование SCM
Классы и серверы
Обобщения
Оптимизации
Снова интерфейс и реализация
Моникеры и композиция
Моникеры и сохраняемость
Время жизни сервера
Классы и IDL
Эмуляция классов
Категории компонентов
Где мы находимся?
|