Обычно приложение состоит из одного монолитного двоичного файла. После того, как приложение сгенерировано компилятором, оно остается неизменным — пока не будет скомпилирована и поставлена пользователю новая версия. Чтобы учесть изменения в операционных системах, аппаратуре и желаниях пользователей, приходится ждать перекомпиляции. Приложение застывает, подобное скале посреди реки перемен. И по мере того, как вся индустрия программирования стремительно уходит все дальше в будущее, оно стареет — и устаревает.
При современных темпах развития индустрии программирования приложениям нельзя оставаться застывшими. Разработчики должны найти способ вдохнуть новую жизнь в программы, которые уже поставлены пользователям. Решение состоит в том, чтобы разбить монолитное приложение на отдельные части, или компоненты (рис. 1-1).
По мере развития технологии компоненты, составляющие приложение, могут заменяться новыми (рис. 1-2). Приложение более не является статичным, обреченным устареть еще до выхода в свет. Вместо этого оно постепенно эволюционирует с заменой старых компонентов новыми. Из существующих компонентов легко создать и абсолютно новые приложения.
Традиционно приложение состояло из отдельных файлов, модулей или классов, которые компилировались и компоновались в единое целое. Разработка приложений из компонентов — так называемых приложений компонентной архитектуры — происходит совершенно иначе. Компонент подобен миниприложению; он поставляется пользователю как двоичный код, скомпилированный, скомпонованный и готовый к использованию. Единого целого больше нет. Его место занимают специализированные компоненты, которые подключаются во время выполнения к другим компонентам, формируя приложение. Модификация или расширение приложения сводится просто к замене одного из составляющих его компонентов новой версией.
Для того, чтобы разбить монолитное приложение на компоненты, необходим мощный инструмент. Инструмент, который мы будем использовать, называется СОМ. СОМ — модель компонентных объектов (Component Object Model) — это спецификация метода создания компонентов и построения из них приложений. Более четырех лет назад СОМ была разработана в Microsoft, чтобы сделать программные продукты фирмы более гибкими, динамичными и настраиваемыми. Практически все продаваемые сегодня приложения Microsoft используют СОМ. Технология ActiveX этой фирмы построена на основе компонентов СОМ.
Эта книга посвящена созданию компонентов СОМ с помощью C++. Изучая примеры программ, Вы увидите, как построить компоненты СОМ, которые объединяются в приложения, способные не только работать, но и с течением времени расти и развиваться.
Прежде чем перейти к подробному изучению СОМ, посмотрим, какие выгоды дает компонентная архитектура и что необходимо для создания приложений из компонентов.
Мы уже упоминали одно из преимуществ компонентных архитектур — способность приложения эволюционировать с течением времени. Кроме удобства и гибкости при модернизации существующих приложений, создание программ из компонентов имеет другие достоинства. Они связаны с адаптацией приложений к нуждам пользователя, библиотеками компонентов и распределенными компонентами.
Пользователи часто хотят подстроить приложения к своим нуждам, точно так же, как мы подбираем домашнюю обстановку к своим вкусам. Конечные пользователи предпочитают, чтобы приложение работало так, как они привыкли. Программистам в корпорациях нужны адаптируемые приложения, чтобы создавать специализированные решения на основе готовых продуктов. Компонентные архитектуры хорошо приспособлены для адаптации, так как любой компонент можно заменить другим, более соответствующим потребностям пользователя.
Предположим, что у нас есть компоненты на основе редакторов vi и Emacs. Как видно из рис. 1-3, пользователь 1 может настроить приложение на использование vi, тогда как пользователь 2 — предпочесть Emacs. Приложения можно легко настраивать, добавляя новые компоненты или заменяя имеющиеся.
Одна из самых многообещающих сторон внедрения компонентной архитектуры — быстрая разработка приложений. Если наши ожидания сбудутся, Вы сможете выбирать компоненты из библиотеки и составлять из них, как из деталей конструктора, цельные приложения (рис. 1-4).
Сборка приложений из стандартных блоков давно была заветной мечтой программистов. Этот процесс уже начался с созданием управляющих элементов ActiveX (ранее известных как управляющие элементы OLE). Программисты на Visual Basic, С, C++ и Java могут воспользоваться управляющими элементами ActiveX для ускорения разработки своих приложений и страниц Web. Конечно, каждому приложению по-прежнему будут нужны и некоторые специализированные компоненты, но в основном можно будет обойтись стандартными.
С возрастанием производительности и общего значения сетей потребность в приложениях, состоящих из разбросанных по разным местам кусков, будет только повышаться. Компонентная архитектура помогает упростить процесс разработки подобных распределенных приложений. Приложения клиент-сервер — это уже шаг в сторону компонентной архитектуры, поскольку они разделены на две части, клиентскую и серверную.
Создать из обычного приложения распределенное легче, если это обычное приложение состоит из компонентов. Во-первых, оно уже разделено на функциональные части, которые могут располагаться вдали друг от друга. Во-вторых, поскольку компоненты заменяемы, вместо некоторого компонента можно подставить другой, единственной задачей которого будет обеспечивать связь с удаленным компонентом. Например, на рис. 1-5 компонент С и компонент D расположены в сети на двух удаленных машинах. На локальной машине они заменяются двумя новыми компонентами, переадресовщиками С и D. Последние переправляют запросы от других компонентов к удаленным С и D по сети. Для приложения на локальной машине неважно, что настоящие компоненты С и D где-то в другом месте. Точно так же для самих удаленных компонентов не имеет значения их расположение. При наличии подходящих переадресующих компонентов приложение может совершенно игнорировать фактическое местоположение своих частей.
Теперь, когда мы познакомились с некоторыми достоинствами компонентов, посмотрим, что требуется для их создания. Затем мы остановимся на роли, которую в создании компонентов играет СОМ.
Преимущества использования компонентов непосредственно вытекают из способности последних подключаться к приложению и отключаться от него. Для этого компоненты должны удовлетворять двум требованиям. Вопервых, они должны компоноваться динамически. Во-вторых, они должны скрывать (или инкапсулировать) детали своей реализации. Попытка определить, какое из этих требований важнее, приводит к дилемме курицы и яйца. Каждое из этих требований зависит от другого. Я склонен считать, что критически важна динамическая компоновка, а сокрытие информации есть ее необходимое условие. Давайте рассмотрим эти требования более подробно.
Наша конечная цель — предоставить пользователю возможность заменять компоненты во время работы приложения. Хотя эта возможность не всегда реализуется, хотелось бы иметь для нее поддержку. Поддержка замены компонента во время выполнения требует динамической компоновки.
Чтобы понять, как это важно, лучше всего представить себе приложение, построенное из компонентов, которые не могут объединяться во время выполнения. Если Вы захотите изменить один из компонентов такой системы, Вам придется статически перекомпоновать или перекомпилировать программу и заново разослать ее пользователям. В конце концов, нельзя требовать от конечных пользователей, чтобы они перекомпоновывали приложение самостоятельно. Даже если они знают, как это сделать, у них скорее всего нет компоновщика — либо вообще, либо конкретного, необходимого. Приложение, собранное из компонентов, которые надо статически перекомпоновывать каждый раз при замене одного из них, эквивалентно приложению-монолиту.
Теперь давайте разберемся, почему динамическая компоновка требует инкапсуляции. Чтобы сформировать приложение, компоненты подключаются друг к другу. Если Вы хотите заменить один из компонентов, надо отключить от системы старый и подсоединить новый. Новый компонент должен подсоединяться тем же способом, что и старый, иначе компоненты придется переписывать, перекомпилировать и перекомпоновывать. Неважно, поддерживает ли компоненты и приложение динамическую компоновку. Изменив способ подключения компонента к другим компонентам, Вы разрушаете всю систему и должны перекомпилировать, если не переписать, все заново.
Чтобы понять, как это связано с инкапсуляцией, нам необходимо определить некоторые термины. Программа или компонент, использующие другой компонент, называется клиентом (client). Клиент подсоединяется к компоненту через интерфейс (interface). Если компонент изменяется без изменения интерфейса, то изменений в клиенте не потребуется. Аналогично если клиент изменяется без изменения интерфейса, то нет необходимости изменять компонент. Однако если изменение либо клиента, либо компонента вызывает изменение интерфейса, то и другую сторону интерфейса также необходимо изменить.
Таким образом, для того, чтобы воспользоваться преимуществами динамической компоновки, компоненты и клиенты должны стараться не изменять свои интерфейсы. Они должны быть инкапсулирующими. Детали реализации клиента или компонента не должны отражаться в интерфейсе. Чем надежнее интерфейс изолирован от реализации, тем менее вероятно, что он изменится при модификации клиента или компонента. Если интерфейс не изменяется, то изменение компонента оказывает лишь незначительное влияние на приложением целом.
Необходимость изоляции клиента от деталей реализации накладывает на компоненты ряд важных ограничений. Ниже приведен список этих ограничений:
Давайте рассмотрим некоторые из этих пунктов подробнее.
Многие не считают нужным требовать от компонентов независимости от языка программирования, как это сделано выше. Для обоснования своей позиции предположим, что у нас есть приложение, которое можно настраивать и перестраивать только при помощи компонентов, написанных на Objective С. Желающих писать для нас компоненты найдется немного, так как большинство программистов пользуются C++. Через некоторое время ,ды поймем, что никто не собирается писать для нашего приложения компоненты, и зададим в качестве рабочего языка C++. В результате у приложения появится значительно больше компонентов. Однако тут родится мода на новый язык, скажем, EspressoBeans, и все перейдут на него, оставляя компиляторы C++ пылиться без дела. Чтобы не сойти с дистанции, мы тоже потребуем писать компоненты на EspressoBeans. Итак, к этому моменту у нас будет три совершенно разных варианта создания компонентов для нашего приложения. Тут мы как раз и разоримся. Оказывается, в нашем сегменте рынка доминирует Visual Basic. Наш конкурент предоставил своим клиентам возможность писать компоненты на любом языке, включая Visual Basic, и по-прежнему процветает.
Если архитектура не зависит от языка, то компоненты может писать кто угодно. Они не будут устаревать при появлении новых языков. Такая архитектура обеспечит нам успех на рынке.
Пользователь может иметь два клиентских приложения, использующих один и тот же компонент. Предположим, что одно приложение применяет новую версию этого компонента, а другое — старую. Установка новой версии не должна нарушать работу приложения, которое использовало старую версию. На рис. 1-6 старое приложение использует новый компонент vi абсолютно так же, как это делает новое.
Однако обратная совместимость не должна ограничивать развитие компонентов. Нужно, чтобы поведение компонента для новых приложений можно было радикально изменять, не нарушая поддержку старых приложений.
Теперь посмотрим, как СОМ согласуется с этими требованиями.
Спецификация СОМ (СОМ Specification) — это документ, который устанавливает стандарт для нашей компонентной архитектуры. Компоненты, которые мы будем разрабатывать в этой книге, следуют данному стандарту. Спецификация СОМ содержится на прилагаемом к книге компакт-диске. Однако Вы, вероятно, уже хотите точно знать, что же такое компоненты СОМ.
Компоненты СОМ состоят из исполняемого кода, распространяемого в виде динамически компонуемых библиотек (DLL) или ЕХЕ-файлов Win32. Компоненты, написанные в соответствии со стандартом СОМ, удовлетворяют всем требованиям компонентной архитектуры.
Компоненты СОМ подключаются друг к другу динамически. Для этой цели СОМ использует DLL. Но, как мы видели, сама по себе динамическая компоновка не обеспечивает компонентной архитектуры. Компоненты должны быть инкапсулированы.
Инкапсуляция в компонентах СОМ достигается легко, поскольку они удовлетворяют нашим ограничениям:
Компоненты СОМ объявляют о своем присутствии стандартным способом. Используя схему объявлений СОМ, клиенты могут динамически находить нужные им компоненты.
Компоненты СОМ — отличный способ предоставления объектно-ориентированных API или сервисов другим приложениям. Они прекрасно подходят и для создания библиотек, не зависящих от языка программирования компонентов, из которых можно быстро строить новые приложения.
Про многие вещи можно сказать «это - СОМ», но есть и много вещей, которые к СОМ не относятся, хотя их часто путают.
СОМ - это не язык программирования. СОМ - не конкурент языкам программирования. Спор о том, что лучше — C++ или СОМ, не имеет смысла; у них разное назначение. СОМ указывает нам, как писать компоненты. При этом мы свободны в выборе языка. В данной книге для разработки компонентов используется исключительно C++.
СОМ не является также конкурентом или заменой DLL. СОМ использует их для динамического объединения компонентов. Однако, по моему мнению, СОМ дает наилучший способ использовать возможности DLL. Любая проблема, которую можно решить при помощи DLL, лучше решается с компонентами СОМ. Я бы вообще не использовал DLL иначе как для поддержки компонентов СОМ. Это показывает, как эффективно СОМ использует DLL.
СОМ, по своей сути, - это не API или набор функций, подобный API Win32. СОМ не предоставляет таких сервисов, как MoveWindow и т. п. (Тем не менее, СОМ предоставляет некоторые сервисы управления компонентами, которые описаны ниже.) Напротив, СОМ — это способ создания компонентов, которые могут предоставлять сервисы через объектно-ориентированные API. СОМ — и не библиотека классов C++, подобная MFC. СОМ обеспечивает способ разработки библиотек компонентов, независимых от языка программирования, но не занимается собственно разработкой.
СОМ — это больше, чем просто спецификация: в состав СОМ входит и некоторое количество кода. В СОМ есть API; это библиотека СОМ, предоставляющая сервисы управления компонентами, которые полезны всем клиентам и компонентам. Большинство функций этого API несложно ревизовать самостоятельно, если Вы разрабатываете компоненты в стиле СОМ не для Windows. Библиотека СОМ создана, чтобы гарантировать единообразное выполнение всеми компонентами наиболее важных операций. Она экономит время разработчикам, создающим собственные компоненты и клиенты. Большая часть кода в библиотеке СОМ служит для поддержки распределенных или сетевых компонентов. Реализация распределенной СОМ (Distributed СОМ, DCOM) в системах Windows предоставляет код, необходимый для обмена информацией с компонентами по сети. Это избавляет Вас не только от необходимости писать такой код, но и от необходимости знать, как это делать.
Мне больше всего нравится трактовка СОМ как стиля программирования. Этот стиль можно использовать в рамках любой операционной системы и, как я уже говорил, любого языка программирования. Для этого Вам не нужен никакой Windows-специфичный код СОМ. Примеры в первых восьми главах этой книги легко модифицировать так, чтобы вообще не использовать код Windows. СОМ — это воплощение концепций компонентного программирования. Подобно структурному или объектно-ориентированному программированию, СОМ — это способ организации программ. Концепции правильного проектирования программ входят в спецификацию СОМ как составная часть.
СОМ удовлетворяет всем требованиям к компонентной архитектуре, которые обсуждались ранее. СОМ использует DLL для поддержки компонентов, которые могут взаимозаменяться во время выполнения. СОМ гарантирует, что такие компоненты могут воспользоваться всеми преимуществами динамической компоновки, посредством:
СОМ обеспечивает строгое отделение клиента от компонента. Именно в этой строгой изоляции заключена сила СОМ.
Теперь те, кому это интересно, могут познакомиться с историей СОМ.
Краткая история мира СОМ |
СОМ разрабатывалась для того, чтобы сделать приложения более настраиваемыми и гибкими. Первоначальной целью была поддержка концепции, известной как связывание и внедрение объектов (object linking and embedding). Идея заключалась в том, чтобы обеспечить документоориентированное представление мира, когда, например. Вы можете редактировать электронную таблицу из текстового процессора. Реализация связывания и внедрения объектов, созданная в Microsoft, получила название OLE. Первая версия OLE для связи между клиентом и компонентом использовала аппарат, известный как динамический обмен данными (dynamic data exchange - DDE). B OLE 1 не было СОМ. DDE был построен на основе архитектуры передачи сообщений Windows. Самое лучшее, что я могу сказать об OLE 1, — этот инструмент работает, точнее, более или менее работает. DDE медлителен. Написать корректно работающий код DDE сложно. Вдобавок, DDE не слишком надежен и гибок. Излишне говорить, что требовалось изобрести что-нибудь получше.
Решением стала СОМ. СОМ меньше, быстрее, гибче и надежнее DDE. Вторая версия OLE была переписана с использованием СОМ вместо DDE. СОМ стала новым фундаментом, на котором выстроены конструкции OLE. Однако OLE — первая система на основе СОМ, и уже поэтому она дает не лучший пример использования возможностей СОМ. У OLE репутация сложного, медленного и трудного для программирования аппарата. Это недостатки реализации, а не СОМ. Напомню еще раз, OLE — это первая попытка сделать то, чего раньше никто не делал. С помощью OLE можно поместить картинку, созданную графическим редактором одного производителя, в текстовый редактор другого и модифицировать прямо там. Создатели OLE старались реализовать все возможности без какихлибо ограничений. Вместо того, чтобы вынуждать клиента и компонента установить ограниченную связь, OLE определяет избыточную связь. Компонент может делать практически все, что угодно, а клиент должен быть к этому готов. Это делает OLE очень трудным для программирования. Скоро появятся новые продукты Microsoft на основе СОМ, и некоторые из них просто изумительны. Несомненно, другие разработчики тоже пишут компоненты СОМ. СОМ ждет блестящее будущее! |
Технология производства программного обеспечения развивается слишком быстро, чтобы позволить Вашему приложению год или два ждать модернизации. .Эту проблему решает разделение приложения на отдельные маленькие приложения, или компоненты, которые объединяются в единое целое во время выполнения. Компоненты можно модернизировать независимо друг от друга, что позволяет приложению эволюционировать с течением времени.
СОМ определяет стандартный способ написания компонентов. Компоненты, написанные в соответствии со стандартом СОМ, можно объединять в приложение. При этом не имеет значения, кем они написаны и как реализованы. Компонент СОМ можно использовать вместе с другими компонентами. Ключевой момент в обеспечении взаимозаменяемости компонентов — инкапсуляция. СОМ обеспечивает ее, уделяя основное внимание соединению, или интерфейсу между компонентом и клиентом. В следующей главе мы увидим, почему для компонентов так важны интерфейсы. Мы также посмотрим, как реализуется интерфейс СОМ.