- •Государственный комитет рф по высшему образованию
- •0. Введение.
- •0.1. Идея общей интеграции.
- •0.2. Взаимодействие на уровне процедур.
- •0.3. Распределенные объекты.
- •0.4. Почему corba.
- •1. Поддержка на различных платформах.
- •2. Устойчивость стандарта.
- •3. Сложность освоения.
- •4. Поддержка повторного использования кода.
- •1. Постановка задачи.
- •1.1. Классические объекты.
- •1.2. Распределенные объекты в терминах спецификации corba.
- •1.3. Требования, предъявляемые к orb-у.
- •2. СпецификацияCorba.
- •2.1. Объектная модель.
- •2.2. Обзор архитектурыCorba.
- •2.3. Пример Брокеров Объектных Запросов.
- •3. Структура системы.
- •3.1. Уточнение деталей реализации.
- •3.2. Структура ядра системы.
- •3.3. Структура библиотеки.
- •3.4.Структура подсистемы обработки запросов.
- •3.5. Входные и выходные данные.
- •4. Протокол обменаGiop.
- •4.1. Особенности и цели протокола.
- •4.2. Обзор протоколаGiop.
- •4.3. Синтаксис Общего Представления Данных -cdr.
- •4.4. Формат сообщений протокола giop.
- •4.5. Транспорт для протоколаGiop.
- •4.6. Реализация взаимодействия по протоколуGiop.
- •4.7. Поддержка протоколаGiop в рамках отображения дляObject Pascal.
- •5. Разработка отображения для языкаObject Pascal.
- •5.1. Множественное наследование.
- •5.2. Статические экземпляры классов.
- •Initialization
- •Initialization
- •6. Технология написания и отладки приложений, работающих с распределенными объектами.
- •6.1. Этапы разработки программы.
- •6.2. Технология написания сервера объекта.
- •6.3. Технология написания клиента объекта.
- •6.4. Отладочные возможности библиотеки.
- •7. Пример программы, работающей с распределенными объектами.
- •7.1. Последовательность действий при создании объекта.
- •7.2. Объект библиотека.
- •7.3. Сервер объекта.
- •7.3. Клиент объекта.
- •7.4. Окончательный результат.
- •8. Анализ конкурентоспособности программного продукта.
- •8.1. Введение.
- •8.2. Ситуация на рынке.
- •8.3. Программные продукты - конкуренты.
- •8.4. Основные понятия.
- •8.5. Параметры для оценки эффективности.
- •8.6. Расчет эффективности.
- •8.7. Цена.
- •8.8. Конкурентоспособность.
- •8.9. Выводы и прогнозы.
- •9. Вопросы эргономики и их решение для создания комфортных условий труда программистов.
- •9.1. Введение.
- •9.2. Рабочее место программиста.
- •9.3. Вредные факторы, присутствующие на рабочем месте и их классификация.
- •9.4. Вредные производственные воздействия.
- •9.5. Эргономические требования.
- •9.6. Эргономика окружающей среды.
- •9.7. Экологическая безопасность.
- •9.8. Выводы.
5. Разработка отображения для языкаObject Pascal.
При обеспечении возможности работы с системой из интегрированной среды разработки Delphi возникла потребность разработки отображения языкаIDL для языкаObject Pascal, используемого вDelphi и являющегося дальнейшим развитием языкаTurbo Pascal, используемого в первых компиляторах фирмыBorland International. При разработки отображения пришлось столкнуться с рядом проблем, которые были успешно преодолены. Далее в этой главе описаны наиболее показательные проблемы, которые нельзя было преодолеть стандартными средствами языка и способы, которыми они были решены.
5.1. Множественное наследование.
При разработке отображения для языка программирования Object Pascal возникла проблема с реализацией множественного наследования. СпецификацияCORBAдопускает множественное наследование объектов. Поэтому возникла потребность в эмуляции множественного наследования. Рассмотрим основные характеристики множественного наследования и их реализацию.
Несколько базовых классов.
На рисунке 5-1 показан пример множественного наследования. КлассClassC наследуется сразу от двух классов - классаClassA и классаClassB.
Рисунок 5-1. Множественное наследование.
Само множественное наследования реализовано следующим образом. Если некоторый класс (назовем его основным) наследуется от двух и более классов, то он на самом деле наследуется от первого класса, а для остальных классов он является контейнером, то есть объекты этих классов просто определяются как поля нового класса. Экземпляры этих (дополнительных) классов создаются в конструкторе при создании основного класса и уничтожаются при его уничтожении. Рисунок 5-2 иллюстрирует сказанное.
Рисунок 5-2. Псевдомножественное наследование вObject Pascal.
Таким образом новый класс инкапсулирует все данные базовых классов. Для обеспечения же инкапсуляции и методов базовых классов нужно предпринять некоторые дополнительные действия.
Наследование методов базовых классов.
Предположим, что класс ClassA имеет методMethodA, а классClassB - методMethodB. Тогда классClassC, производный от этих двух классов автоматически наследует методы от обоих классов, то есть у объектов этого класса можно вызывать как методMethodA так и методMethodB(рисунок5-3).
Рисунок 5-3. Наследование методов.
В языке Object Pascal у основного класса создаются все методы, которые имеются у дополнительных классов с теми же самыми параметрами. При вызове метода просто осуществляется перевызов метода у соответствующего инкапсулированного объекта (рисунок5-4).
Рисунок 5-4. Наследование методов вObject Pascal.
Так как IDL, используемый в спецификацииCORBAне позволяет определять у класса поля, то этот аспект эмуляции множественного наследования был благополучно проигнорирован. Вместо полейCORBA определяет так называемые атрибуты, которые прекрасно ложатся к концепцию свойствObject Pascal, для которых все производится аналогично функциям.
Преобразование типов.
Преобразование типов вызвало наибольшие затруднения. В рассмотренной ситуации если имеется экземпляр объекта класса ClassC, то он может быть преобразован как к типуClassA, так и к типуClassB. Такое преобразование называется преобразованием вниз по дереву наследования и доступно в любом случае.
Далее, если имеется переменная, указывающая на класс ClassAили классClassB, то можно попытаться выполнить преобразование к типуClassC и в том случае, если такое преобразование возможно (то есть в действительности объект классаClassC был преобразован к одному из своих базовых классов), то в результате преобразования должна возвратиться ссылка на объект классаClassC.Такое преобразование называется преобразованием вверх по дереву наследования и проверка корректности преобразования возможно только в том случае, если компилятор допускает получение информации о типе в момент выполнения(Run-Time Type Information - RTTI).
В качестве частного случая можно попытаться преобразовать указатель на объект класса ClassA к указателю на объект классаClassB посредством промежуточного преобразования к классуClassC.
Компилятор Delphi поддерживаетRTTI, но разумеется для случая одиночного наследования. Зато на основании этого возможна реализация преобразования типов, удовлетворяющих множественному наследованию.
У базового класса InterfacedObject, от которого далее наследуются все классы имеется свойство (свойство можно рассматривать просто как поле объекта)Instanceтоже типаInterfacedObject, которое указывает на класс-контейнер. При обычном (одиночном) наследовании это поле имеет значениеnil. Если же имеет место эмуляция множественного наследования, то класс контейнер при создании экземпляров дополнительных классов устанавливает это свойство таким образом, чтобы оно указывало на него.
Базовый класс InterfacedObjectтакже объявляет виртуальную функциюAsClass, которая имеет один параметр - тип, к которому надо преобразовать объект. При успешном преобразовании она возвращает указатель на классInterfacedObject, который может быть преобразован к указателю запрашиваемого типа. Если же требуемое преобразование недопустимо, то возникает исключение неправильного преобразования типов. МетодAsClass реализован в классеInterfacedObjectследующим образом:
functionInterfacedObject.AsClass(
Interfaced: InterfacedClass;
AllowNarow: Boolean): InterfacedObject;
begin
Result := nil;
if Self is Interfaced then Result := Self;
if (Result = nil) and (Instance <> nil)
and AllowNarrow
then Result := Instance.AsClass(Interfaced, True);
end;
Не вдаваясь в специфику нововведений языка Object Pascal, касающихсяRTTI, алгоритм можно описать следующим образом. Сначала производится попытка преобразования самого экземпляра объекта к запрашиваемому типу. Если преобразование успешно, то возвращается полученный указатель. В случае же неудачного преобразования проверяется значение свойстваInstance. Если оно имеет значение, отличное отnil, то вызывается методAsClass для класса-контейнера. В противном случае возвращается значениеnil, которое свидетельствует о неудачном преобразовании. ПараметрAllowNarrowнужен для предотвращения зацикливания. Его роль будет рассмотрена несколько позднее.
При таком алгоритме для классов, реализующих одиночное наследование не требуется перекрывать этот метод. Если же класс эмулирует множественное наследование, то в нем следует перекрыть данный метод и в случае неудачной попытки преобразования самого себя в запрашиваемому типу перед обращением к свойству Instanceследует попытаться преобразовать к данному типу один из инкапсулированных объектов. Например, для рассмотренного выше случая методAsClass у классаClassC следует определить следующим образом:
functionClassC.AsClass(
Interfaced: InterfacedClass;
AllowNarow: Boolean): InterfacedObject;
begin
Result := inherited AsClass(Interfaced, False);
if Result = nil
then Result := FClassB.AsClass(Interfaced, False);
if (Result = nil) and (Instance <> nil)
andAllowNarrow
then Result := Instance.AsClass(Interfaced, True);
end;
Нужно заметить, что в первых двух вызовах методов AsClassв качестве второго параметра передается значениеFalse, что предотвращает вызов из этих методов методаAsClass у объекта, на который указывает полеInstance, не допуская этим самым зацикливания. При таком алгоритме возможна некоторая избыточность, которая, однако проявляется только при преобразованиях, эмулирующих множественное наследование. При обычном наследовании лишний код вообще не вызывается.
Описанный подход допускает преобразование к любому из возможных типов, даже для вложенных конструкций - один класс-контейнер является полем у другого, так как при неудаче промежуточных преобразований по цепочке происходит вызов методов AsClass классов-контейнеров.
Для упрощения преобразования типов в каждом классе объявляется статическая функция класса Narrow, которая принимает один параметр типаInterfacedObject и в случае успешного преобразования возвращает указатель на объект, преобразованный к типу класса. Для классаClassC эта функция имеет вид:
class function ClassC.Narrow(
Obj: InterfacedObject): ClassC;
begin
Result := ClassC(Obj.AsClass(ClassC, True));
end;
Тогда для преобразования некоторого объектного типа как вниз, так и вверх по дереву наследования, необходимо вызвать функцию Narrow этого класса:
var
Obj: InterfacedObject;
ObjC: ClassC;
...
ObjC := ClassC.Narrow(Obj);
if ObjC = nil then
begin
{ преобразование недопустимо }
end;
...
Недостатки данной реализации.
Имеющаяся реализация псевдомножественного наследования имеет некоторые недостатки. Самым большим недостатком является отсутствие механизма аналогичного виртуальным базовым классам в языке C++. Смысл виртуальных базовых классов заключается в следующем. Если бы оба наши базовых классаClassA иClassBбыли бы унаследованы еще от некоторого классаClassXобычным образом, то классClassC в конечном счете должен унаследовать две копии объектов этого класса - один через классClassAи один через классClassB(рисунок5-5).
При этом непонятно, каким образом выполнять преобразование объекта класса ClassC к типуClassX - использовать ли копию, находящуюся в классеClassA или классеClassB. ЯзыкC++ в рассмотренной ситуации не допускает выполнение прямого преобразования - необходимо явно преобразовать объект к одному из типовClassA илиClassB, а уже потом выполнять преобразование к типуClassX. Но реализованная эмуляция в данной ситуации выполнит преобразование к первой допустимой копии в порядке наследования классов, то есть к копии, находящейся в классеClassA.
Рисунок 5-5. Статическое наследование.
Язык C++ для устранения данной проблемы предлагает механизм виртуальных базовых классов, который гарантирует, что при любой иерархии наследования экземпляр итогового класса будет иметь только одну копию объекта данного класса. В этом случае дерево наследования будет выглядеть так, как показано на рисунке5-6. При таком наследовании преобразование экземпляра классаClassC к типуClassX проблем не вызывает и всегда однозначно разрешается.
Рисунок 5-6. Виртуальное наследование.
Такой способ не поддерживается по нескольким причинам. Во-первых пришлось бы заводить дополнительные механизмы для определения экземпляров виртуальных базовых классов, что представляет определенные трудности и в результате требовало бы сложного алгоритма определения местонахождения копии объекта требуемого класса. Во-вторых спецификация CORBAникак не определяет механизм виртуального наследования, везде предполагая статическое. Следовательно, все доступныеORB-у классы для обеспечения ожидаемого от них поведения должны наследоваться статически, так как в случае наследования распределенных классов виртуальное наследование вызвало бы массу проблем. Таким образом, виртуальное наследование ввиду его громоздкости и отсутствия потребности в этом при обеспечении отображения для языкаObject Pascal не реализовано.