Скачиваний:
100
Добавлен:
01.05.2014
Размер:
1.56 Mб
Скачать

Использование фабрики классов

Пример, который находится здесь, иллюстрирует "концепцию фабрики класса", а более точно - код показывает, как могут быть взаимосвязанно реализованы статические и нестатические аспекты типа. Построен он на базе предыдущего примера - там был сделан почти настоящийCOM-сервер. Были еще два "элементарныхCOM-объекта", которые к существовавшим у нас типамBanzaiиHelloстали реализовывать "фабрики класса", а сами существовавшие у нас сущности стали "объектами экземпляров". Сейчас можно утверждать, что в данном примере получился простой, очень даже примитивный (на грани того, что из него больше ничего не выкинуть), но - самый настоящийCOM-сервер, реализующий два статических типа. Эффективность кода примера - не то обстоятельство, на которое нужно сейчас обращать внимание. Во многих случаях можно было сделать короче (чего только стоят четыре оконных процедуры в клиенте, когда можно было обойтись двумя). Но "короче" и "очевиднее" вещи, во многом, противоположные. Поэтому, там где можно было "коротко" или "длинно" предпочтение отдавалось "понятно", хотя в реальных программах, вы, конечно, многое оптимизируете.

Клиент, в целом, остался тот же - к "маленькому окну" привязывается COM-объект и посредством графического интерфейса можно наглядно выяснить существующие отношения между объектами. Добавилось новое - в соответствии с тем, что у нас теперь есть не только "объекты экземпляра", но и "объекты типа" мы имеем не два, а четыре "маленьких окна" - на каждый тип есть окно "статического типа" и окно "экземпляра". При этом "окно экземпляра" функционально осталось точно таким же, каким оно было в предыдущем примере - функциональность самих объектов экземпляра не изменилась. А вот порождаются "объекты экземпляра" теперь по-другому - через "объект статического типа". Поэтому в "окне статического типа" показываются кнопки для вызова методов интерфейсаIUnknown(как вы помните - это совсем не тотIUnknown, который реализован в "объекте экземпляра") и для вызова методаCreateInstance. Тем, кто интересуется как создать немодальный диалог и передать ему параметр и пр. - клиентская часть примера тоже может быть полезна. К сожалению, именно такой код и составляет большую часть примера. Код, вызывающийCOMи обрабатывающий вызовы методов находится в численном меньшинстве. Что, может быть, и не так плохо - это наглядное подтверждение, что затраты собственно клиента на обращение сCOM-объектом даже "напрямую" не так уж и велики, как это часто кажется.

В прошлых примерах мы рассматривали интересное явление - объект, который экспонирует интерфейс, может быть для программы-владелицы порожден и в динамической и в статической памяти. Для программы-клиента эту разницу заметить невозможно, а вот программа-сервер в одном случае будет "шлёпать" всякий раз новые экземпляры объектов, а в другом всем будет выдавать одну и ту же ссылку. Это - в чистом виде артефакт реализации, он к COMне имеет никакого отношения, и у нас объектBanzaiпорождался в статической памяти, а объектHello- в динамической как пример того, что это возможно. Некогда нАчатая, в данном примере эта особенность углУблена - в реализации статического типаBanzaiтот элементарныйCOM-объект, что реализует статические аспекты типа (фабрика класса) размещается в динамической памяти, а сам "объект экземпляра" - в статической. Для статического типаHelloсделано наоборот - "фабрика класса" является объектом в статической памяти, а экземпляры она размещает в динамической. Мне не хватает ещё двух статических типов, чтобы реализовать оставшуюся пару сочетаний, но я думаю, - как это можно сделать уже видно. Так что правильное представление о реализации всего спектра у вас сформИруется. Я надеюсь.

Важно, что мы, наконец, можем рассмотреть и действие функции APICoCreateInstance- именно она, получаяCLSID-IIDвыдаёт клиенту ссылку на экземпляр объекта. Именно она чаще всего упоминается в литературе как "источник ссылки" наCOMобъект. Но функция эта - с лукавинкой. Ничего не зная о том, что такоеCOM, имея о нём поверхностное представление как просто о двоичной реализацииООП, "естественно" придти к заключению, что именно она и есть некий аналог оператора new. Сейчас, уже зная оCOMвполне достаточно, вы никогда не придёте к такому выводу! На самом деле, принимая на входCLSID-IID,CoCreateInstanceпоследовательно вызываетGoGetClassObjectс аргументамиCLSID-IClassFactoryи у полученного "объекта типа" запрашивает ссылку на тот интерфейс, что указанIID. "Объект типа" после этого освобождается. Для внешнего наблюдателя складывается впечатление, что он и не создавался. Но это - не так. Если трассировать всё события, то это можно увидеть. А наш пример эти события как раз и трассирует, так что, как работаетCoCreateInstance"изнутри" вы сможете увидеть воочию.

Всё имеет вполне рациональное объяснение. Во-первых, в очень многих случаях "объект типа" как раз и создаётся только для того, чтобы получить единственную ссылку на "объект экземпляра". И для большего - не нужен. Задача типовая. Значит, переложив её на систему, можно хоть немного облегчить клиента... Облегчение это маленькое, но ведь и клиентов, которые выполняют именно такую последовательность действий - много. Так что совокупная экономия может быть существенной. Во-вторых, и это пока для нас неочевидно (не дошли ещё по порядку изложения) запрос на ссылку на экземпляр может поступить из другого процесса, а то и с другой машины. И "перегонка" указателя на промежуточный и сугубо локальный "объект типа" от сервера к клиенту с полномасштабным маршалированием - ровно вдвое снижает эффективность вызова. Поэтому исполнение "типовой последовательности действий" локально опять выгодно. Вот почему функция CoCreateInstanceприсутствует в системе. Но, конечно, если вам нужно получить не одну, а несколько ссылок на экземпляры одного и того же статического типа, серия вызововCoCreateInstanceстановится менее выгодной, чем раздельное получение ссылки на объект типа поCoGetClassObjectи явный вызовIClassFactory::CreateInstance. Чем когда пользоваться должно быть понятно.

Наш пример, в той же технологии, как и предыдущий пример показывает транспаранты о том, что внутри сервера произошло событие. Я удалил транспаранты, связанные с работой DllRegisterServerи других экспортируемых функций, но оставил транспарант, который показывает, когда удаляется блок памяти - вы должны увидеть какая внутренняя работа совершается при вполне внешне "невинных" действиях. А как загружается и выгружается сервер вы видели из прошлого примера.

На что следует обратить внимание? На то, что вы должны сами явно вызывать Release. Сделано это для того, чтобы вы могли увидеть как связаны элементарныеCOM-объекты, которые реализуют статические и нестатические аспекты типа (ещё и поэтому "фабрика класса" статического типаBanzaiсделана не "как обычно"). Например, вы можете получить ссылку на объект типа, создать экземпляр, уничтожить объект типа и продолжать использовать экземпляр...

Для облегчения понимания "кто есть who" в состав диалогов введены дополнительные поля - они показывают численное значение адреса интерфейса COM-объекта, который "привязан" к этому "маленькому окну". Сделано это очень просто - желающие убедиться могут заглянуть в код, который обрабатывает событиеWM_INITDIALOG.

Обратите внимение на то, каково будет это численное значение когда вы создаёте новый экземпляр объекта или просто клонируете ссылку. Обратите особенное внимание - когда адрес меняется? Например, для статического типа Hello"объект типа" реализован в статической памяти сервера, поэтому сколько бы вы ни запрашивали ссылок на "объект типа" сервер всё время будет возвращать одно и то же численное значение. А вот "объект экземпляра" у этого типа реализован в динамической памяти сервера, поэтому каждое новое создание экземпляра будет давать и новый адрес ссылки. Клонирование ссылки, естественно, нового объекта не создаёт - поэтому при клонировании любой ссылки адрес не будет изменяться, как бы объект ни был реализован.

Посмотрите, что произойдёт, если в любом месте у любого объекта на один раз больше вызвать Release, нежелиAddRef. Не забывайте только, что "объект типа" статического типаHelloи "объект экземпляра" статического типаBanzaiреализованы как статические объекты сервера, т.е. "на нихReleaseне действует".

ПредыдущаяОглавлениеСледующая

ПредыдущаяОглавлениеСледующая