Дональд Бокс - Сущность технологии СОМ. Библиотека программиста Страница 42

Тут можно читать бесплатно Дональд Бокс - Сущность технологии СОМ. Библиотека программиста. Жанр: Компьютеры и Интернет / Программирование, год -. Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте «WorldBooks (МирКниг)» или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста краткое содержание

Прочтите описание перед тем, как прочитать онлайн книгу «Дональд Бокс - Сущность технологии СОМ. Библиотека программиста» бесплатно полную версию:
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста читать онлайн бесплатно

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста - читать книгу онлайн бесплатно, автор Дональд Бокс

Узнав обо всех подводных камнях отделяемых интерфейсов, задаешь себе логичный вопрос: "В каких же случаях отделяемые интерфейсы являются подходящими?" Не существует безусловного ответа; в то же время отделяемые интерфейсы очень хороши для поддержки большого числа взаимно исключающих интерфейсов. Рассмотрим случай, в котором в дополнение к трем транспортным интерфейсам, показанным ранее, имеются интерфейсы ITruck (грузовик), IMonsterТruck (грузовик-монстр), IMotorcycle (мотоцикл), IBicycle (велосипед), IUnicycle (уницикл), ISkateboard (скейтборд) и IHelicopter (вертолет), причем все они наследуют IVehicle. Если бы производящий транспортный класс хотел поддерживать любой из этих интерфейсов, но только по одному из них для каждого заданного экземпляра, то для осуществления этого отделяемые интерфейсы были бы прекрасным способом при условии, что главный объект кэшировал бы указатель на первый отделяемый интерфейс. Определение класса главного объекта выглядело бы примерно так:

class GenericVehicle : public IUnknown

{

LONG m_cRef;

IVehicle *m_pTearOff;

// cached ptr to tearoff

// кэшированный указатель на отделяемый интерфейс

GenericVehicle(void) : m_cRef(0), m_pTearOff(0) {}

// IUnknown methods

// методы IUnknown

STDMETHODIMP QueryInterface(REFIID, void **);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release (void);

// define tearoff classes

// определяем классы отделяемых интерфейсов

class XTruck : public ITruck { … };

class XMonsterTruck : public IMonsterTruck { … };

class XBicycle : public IBicycle { … };

:

:

:

};

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

STDMETHODIMP GenericVehicle::QueryInterface(REFIID riid ,void **ppv)

{ if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(this);

else if (riid == IID_ITruck) { if (m_pTearOff == 0)

// no tearoff yet, make one

// отделяемого интерфейса еще нет, создаем один

m_pTearOff = new XTruck(this);

if (m_pTearOff)

// tearoff exists, let tearoff QI

// отделяемый интерфейс существует, пусть это QI

return m_pTearOff->QueryInterface(riid, ppv);

else

// memory allocation failure

// ошибка выделения памяти

return (*ppv = 0), E_NOINTERFACE;

}

else if (riid == IID_IMonsterTruck)

{

if (in_pTearOff == 0)

// no tearoff yet, make one

// отделяемого интерфейса еще нет, создаем один

m_pTearOff = new XMonsterTruck(this);

if (m_pTearOff)

// tearoff exists, let tearoff QI

// отделяемый интерфейс существует, пусть это QI

return m_pTearOff->QueryInterface(riid, ppv);

else

// memory allocation failure

// ошибка выделения памяти

return (*ppv = 0), E_NOINTERFACE;

}

else …

:

:

:

}

На основе показанной здесь реализации QueryInterface на каждый объект будет приходиться по большей части по одному отделяемому интерфейсу. Это значит, что в случае отсутствия запросов на транспортные интерфейсы объект будет тратить в сумме 12 байт (vptr IUnknown + счетчик ссылок + кэшированный указатель на отделяемый интерфейс). Если транспортный интерфейс запрошен, то объект будет тратить в сумме от 24 до 28 байт (исходные 12 байт + наследующий Vehicle vptr + счетчик ссылок + обратный указатель на главный объект + (необязательно) служебная запись malloc (memory allocation – выделение памяти)).

Если бы в данном случае отделяемые интерфейсы не использовались, то определение класса выглядело бы примерно так:

class GenericVehicle : public ITruck, public IHelicopter, public IBoat, public ICar, public IMonsterTruck, public IBicycle, public IMotorcycle, public ICar, public IPlane, public ISkateboard { LONG m_cRef;

// IUnknown methods – методы IUnknown

:

:

:

};

В результате этот класс создал бы объекты, тратящие всегда 44 байта (десять vptr + счетчик ссылок). Хотя производящий класс может показаться немного запутанным, постоянные интерфейсы СОМ принадлежат к аналогичной категории, так как в настоящее время существует восемь различных постоянных интерфейсов, но объект обычно выставляет только один из них на экземпляр. В то же время разработчик класса не всегда может предсказать, какой из интерфейсов будет запрошен определенным клиентом (и будет ли какой-либо). Кроме того, каждый из восьми интерфейсов требует своего набора поддерживающих элементов данных для корректной реализации методов интерфейса. Если эти элементы данных были созданы как часть отделяемого интерфейса, а не главного объекта, то для каждого объекта будет назначен только один набор элементов данных. Этот тип сценария идеален для отделяемых интерфейсов, но опять же, для большей эффективности, указатель на отделяемый интерфейс следует кэшировать в главном объекте.

Двоичная композиция

Композиция и отделяемые интерфейсы – это две технологии на уровне исходного кода, предназначенные для реализации объектов СОМ на C++. Обе эти технологии требуют, чтобы разработчик объекта имел определения для каждого класса композита или отделяемого интерфейса в исходном коде C++, для возможности обработать подобъект, прежде чем возвратить его посредством QueryInterface. Для ряда ситуаций это очень разумно. В некоторых случаях, однако, было бы удобнее упаковать многократно используемую реализацию одного или большего числа интерфейсов в двоичный компонент, который мог бы обрабатываться через границы DLL, не нуждаясь в исходном коде подкомпонента. Это позволило бы более широкой аудитории повторно использовать подкомпонент, избегая слишком тесной связи с ним, как в случае повторного использования на уровне исходного кода (этот случай описан в главе 1). Однако если компонент повторного использования представляет собой двоичный композит или отделяемый интерфейс, то он должен участвовать в общей идентификации объекта.

Для полного охвата проблем, относящихся к унифицированию идентификации через границы компонентов, рассмотрим следующую простую реализацию ICar:

class Car : public ICar

{

LONG m_cRef; Car(void) : m_cRef(0) {} STDMETHODIMP QueryInterface(REFIID, void **);

STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP GetMaxSpeed(long *pn);

STDMETHODIMP Brake(void); };

STDMETHODIMP Car::QueryInterface(REFIID riid, void **ppv)

{

if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(this);

else if (riid == IID_IVehicle) *ppv = static_cast<IVehicle*>(this);

else if (riid == IID_ICar) *ppv = static_cast<ICar*>(this);

else return (*ppv = 0), E_NOINTERFACE;

((IUnknown*)*ppv)->AddRef();

return S_OK;

}

// car class object's IClassFactory::CreateInstance

// интерфейс IClassFactory::CreateInstance

// объекта класса car

STDMETHODIMP CarClass::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppv)

{

Car *pCar = new Car;

if (*pCar) return (*ppv = 0), E_OUTOFMEMORY;

pCar->AddRef();

HRESULT hr = pCar->QueryInterface(riid, ppv);

pCar->Release(); return hr;

}

Этот класс просто использует фактические реализации QueryInterface, AddRef и Release.

Рассмотрим второй класс C++, который пытается использовать реализацию Car как двоичный композит:

class CarBoat : public IBoat

{

LONG m_cRef;

Unknown *m_pUnkCar;

CarBoat(void);

virtual ~CarBoat(void);

STDMETHODIMP QueryInterface(REFIID, void **);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP GetMaxSpeed(long *pn);

STDMETHODIMP Sink(void);

};

Для эмуляции композиции разработчику пришлось бы создать подобъект Car, а деструктору – освободить указатель на подобъект:

CarBoat::CarBoat (void) : m_cRef(0)

{

HRESULT hr = CoCreateInstance(CLSID_Car, 0, CLSCTX_ALL, IID_IUnknown, (void**)&m_pUnkCar);

assert(SUCCEEDED(hr));

}

CarBoat::~CarBoat(void)

{

if (m_pUnkCar) m_pUnkCar->Release();

}

Интересная проблема возникает в реализации QueryInterface:

STDMETHODIMP CarBoat::QueryInterface(REFIID riid, void **ppv)

{

if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(this);

else if (riid == IID_IVehicle) *ppv = static_cast<IVehicle*>(this);

else if (riid == IID_IBoat) *ppv = static_cast<IBoat*>(this);

else if (riid == IID_ICar)

// forward request…

// переадресовываем запрос…

return m_pUnkCar->QueryInterface(riid, ppv);

else return (*ppv = 0), E_NOINTERFACE; ((IUnknown*)*ppv)->AddRef();

return S_OK;

}

Поскольку объект Car не имеет понятия о том, что он является частью идентификационной единицы (identity) другого объекта, то он будет причиной неуспеха любых запросов QueryInterface для IBoat. Это означает, что

QI(IBoat)->ICar

пройдет успешно, а запрос

QI(QI(IBoat)->ICar)->IBoat

потерпит неудачу, так как полученная QueryInterface будет несимметричной. Вдобавок запросы QueryInterface о IUnknown через интерфейсные указатели ICar и IBoat вернут различные значения, а это означает, что будет идентифицировано два различных объекта. Из подобных нарушений протокола IUnknown следует, что объекты CarBoat попросту не являются действительными объектами СОМ.

Идея составления объекта из двоичных композитов звучит красиво. Действительно, Спецификация СОМ четко и подробно указывает, как реализовать эту идею в стандартной и предсказуемой манере. Технология выставления клиенту двоичного подкомпонента непосредственно через QueryInterface называется СОМ-агрегированием. СОМ-агрегирование является лишь набором правил, определяющих отношения между внешним объектом (агрегирующим) и внутренним (агрегируемым). СОМ-агрегирование – это просто набор правил IUnknown, позволяющих более чем одному двоичному компоненту фигурировать в качестве идентификационной единицы (identity) СОМ.

Перейти на страницу:
Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.
Комментарии / Отзывы
    Ничего не найдено.