developing.ru - клуб программистов Здесь может быть и ваша реклама.
developing.ru >технология COM >
Михаил Безверхов
aspid@developing.ru

Бронза и камень... смена материала - смена конструкции

В предыдущей нашей статье был опубликован живой пример "настоящего COM-объекта" с просьбой присылать свои вопросы... вопросов по существу прислано не было. Тогда вопрос возникает у меня - могу ли я полагать, что остальное из нашего изложения не вызывает вообще никаких неясностей?

Если так, то мне и дополнительно рассказывать о том, что мы сделали в примере, в общем-то и нечего. Я бы обратил ваше внимание на несколько заслуживающих упоминания моментов. Во-первых, мы написали очень примитивный, но настоящий COM-объект. И есть смысл, пока содержательные особенности не заслонили просто реализации протокола, сравнить COM-объект с обычным объектом. Точнее, сравнить, что стОит для программиста писать в COM-технологии относительно технологии обыкновенной, с точки зрения его "ручного труда".

Читатели, внимательно изучавшие оба примера, понимают к какому выводу я хочу подвести. У нас есть "обычный класс" из "обычного проекта". Каковы затраты, чтобы преобразовать его формат к COM? Оба примера дают на это совершенно очевидный ответ: 1)нужно разработать абстрактный класс, который бы описывал методы экспонируемые наружу; 2)нужно реализовать IUnknown; 3)в составе сервера нужно реализовать аналог "собственного new" - DllGetClassObject.

Обратите внимание - эти затраты почти постоянны и слабо зависят от сложности объекта с содержательной его стороны. Сколь бы ни был сложен объект, реализация IUnknown у него только одна. Иерархию же абстрактных типов и так рекомендуется разрабатывать уже для средних по размеру "обычных" проектов, чтобы иметь достаточно жёсткий архитектурный каркас. И - это уже из собственного опыта - я редко встречал даже средние приемлемо работающие проекты, где программист бы не покушался на переопределение new/delete хотя бы для одного класса из проекта. Т.е. "чистых затрат" кодирования при написании программы "в COM" относительно "обычного проекта", в общем-то только - реализация IUnknown для каждого COM-объекта, да в клиенте - постоянный вызов AddRef/Release сопровождающий экзистенцию указателя...

Кроме того, как мы когда-то увидим, вовсе не каждый класс C++ в проекте является статическим типом COM, а наоборот - некоторая совокупность классов C++ реализует один COM-объект. Поэтому причина, которая заставляет думать, что "COM - это очень сложно" только одна - нетрадиционная (на данном историческом этапе развития программирования, со временем ситуация изменится на обратную), сравнительно с учебником классического программирования, архитектура. Наверное, играет какую-то роль и то, что в COM как-то уж очень неудобно писать школярские программы наподобие "Hello, world!" - "полезная нагрузка" такой программы явно диссонирует с количеством кода, которое нужно написать для соблюдения просто протокола.

Это - не уникальное явление. Оно уже наблюдалось, когда (по современным меркам - давным-давно) народ сравнивал простую и ясную программу для MS DOS:

int main(){     printf("Hello, world!");     return 0; }

с такой же, но написанной для MS Windows:

int WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPWSTR lpCmdLine,int nShowCmd){         ...     RegisterClass(...);         ...     CreateWindow(...);         ... }   LONG FAR PASCAL MainWndProc(HWND hWnd,unsigned message,WORD wParam,LONG lParam){       switch(message){         ...          case WM_PAINT:         ...                    }      return DefWndProc(...); }

(места жалко, поэтому полный проект такой программы, сгенерированный wizard'ом от Visual Studio, можно извлечь отсюда) и приходил к выводу, что "DOS - намного лучше и эффективнее...". Для "Hello, world!" - безусловно, для значительно же более сложных и развитых программ правильный ответ оказался немного не столь очевидным...

Вторым обстоятельством заслуживающим внимания является то, что собственно "операции поддержки протокола COM" концентрируются только в двух местах - в реализации QueryInterface и в реализации DllGetClassObject. Причём в обоих случаях код получается чисто механическим - при реализации объектом ещё одного интерфейса в QueryInterface добавятся строки сравнения IID и операции приведения типа указателя к данному типу. А если сервер будет реализовывать ещё и другие статические типы, то аналогичные строки добавятся в DllGetClassObject. Это позволяет "высокомеханизировать" написание каркасов COM-объектов. Что, например, и сделано в MFC посредством wizard, а в ATL - посредством соответствующих шаблонов. Также и автоматическое оформление полнофункциональных COM-объектов в Pascal или в Visual Basic самим компилятором опирается на данное обстоятельство, что позволяет программисту концентрироваться на том, на чём и положено - на содержательной части COM-объекта, а не на реализации служебного кода протокола. Добавление же методов в "полезный интерфейс" объекта вообще не требует никаких действий в его "каркасе", как это видно из нашего примера.

Третьим важным обстоятельством является то, что уже и просто "формат COM", сама возможность писать программы в такой "автономной архитектуре", влияет на конструкцию совокупного программного комплекса и - в лучшую сторону. Давайте попробуем произвести обратное преобразование наших простых классов CBanzai и CHello из "компонентной архитектуры" в "обычную". Оно тривиально - нужно удалить реализации IUnknown и DllGetClassObject и слить проекты клиента и сервера, заменив вызов CoCreateInstance на вызов new. Попробуйте это сделать в действительности - это и на самом деле несложно.

Обнаружили "изюмину"? А как же! В клиенте нужно всё время возиться с указателем на объект стремясь его не потерять и где-то (где?) не забыть вызвать к нему delete... и ровно один раз. Мелочь, конечно, но как возможность просто "оставить в покое" указатель в любом месте программы повышает и ясность и, главное, надёжность сложного клиента, когда существует длинная, а то и ветвящаяся последовательность передачи указателя вглубь процедур и от объекта к объекту! Я думаю, счётчик ссылок заслуживает того, чтобы это архитектурное решение применялось и в "обычных" проектах - уж больно оно удачно.

Поэтому, внимательно рассматривая наш пример, можно заподозрить, что основная трудность "компонентной архитектуры" кроется не в дополнительном служебном коде, не в том, что этого кода много или он мало понятен, а - в голове программиста. Он должен иметь соответствующее направление взгляда, видеть "компонентно". Моё собственное убеждение - кодировать на C++COM-сервер или клиент и без применения MFC и ATL столь же (не)трудно, как и писать на нём оконные приложения без применения MFC. Главное - знать, что писать! Производительность труда программиста в данном случае падает только на очень небольших проектах, где действительно кодирование оформления взаимодействия занимает относительно бОльшую часть кода, а собственно "содержательный код" - меньшую. В больших проектах на первый план выходят проблемы архитектуры и необходимость реализации "чего-то" нестандартно, не так, как это принято в реализации средства, в нашем случае - MFC или ATL. Но ведь то же случается и вообще в любом большом проекте и безотносительно к средству, методологии и архитектуре разработки? Хоть это и требует значительно большей квалификации программиста (а для чего вы читаете эти статьи, как не для повышения собственной "потребительной стоимости"?), ещё более дорогим решением окажется, если "системным архитектором" вашего проекта выступит wizard от Visual Studio. Именно это - как сконструировать декомпозицию на модули, а внутри модулей - декомпозицию на объекты и составляет главную трудность компонентного программирования. А - совсем не инструмент, который используется при кодировании... Но - всему своё время. Это - лишь небольшое замечание философской природы, пришедшееся к месту в общем потоке изложения. Мы же, давайте, двинемся пока дальше. Следующая статья открывает новую тему - "COM-серверы".

предыдущий выпускархив и оглавлениеследующий выпуск
Авторские права © 2001 - 2004, Михаил Безверхов
Публикация требует разрешения автора

разделы сайта

форум

технология COM

оптимизация сайтов


© 2000-2004 Клуб программистов developing.ru