ГЛАВА 2

- Понятие исключительной ситуации, ее обработка средствами Delphi
- Обработка RTL-исключений.Иерархия исключений
- Создание собственных исключений
- Интерфейсы и их совместное использование классами
- Интерфейс IUnknown
  - Класс TInterfasedObject
  - Использование оператора as
  - Использование ключевого слова implements
- Использование инрефейсов в распределительных приложениях

Большинство разработчиков на Delphi создают довольно сложные программы. Сложная программа подразумевает разносторонее взаимодействие с операционной системой и приложениями операционной системы. Любое из этих взаимодействий может завершиться неправильно. Примеров этого можно привести очень много, от банального деления на ноль, до открытия несуществующего файла. Обычно для обхода таких ситуаций разработчику приходится вводить многочисленные проверки. Но любой разработчик просто не в состоянии рассмотреть все ситуации, которые могут возникнуть у его программы при взаимодействии с операционной системой и другими приложениями. Именно для таких непредвиденных событий была придумана структурированная обработка исключительных ситуаций. Первоначально она была создана для разработчиков под Windows NT, впоследствии структурированная обработка получила поддержку и в операционных системах Windows 9x. Большинство современных программных сред для создания приложений под Windows поддерживают обработку исключительных ситуаций. Не осталась в стороне и среда Delphi. Обработка исключений была введена уже в Delphi 1.0, но, только начиная с Delphi 2.0, исключения стали частью Win32 API.
В этой главе мы узнаем, что такое исключительная ситуация и как средствами Delphi можно ее обработать. Рассмотрим, что такое RTL-исключения, как можно использовать в обработке исключительных ситуаций метод TAppllcationHandleException. В данной главе мы познакомимся с интерфейсами. Подробно рассмотрим интерфейс IUnknown. А также рассмотрим, как можно использовать интерфейсы в распределенных приложениях.
Понятие исключительной ситуации, ее обработка средствами Delphi
Под исключительной ситуацией мы будем понимать некое непредвиденное событие, способное повлиять на дальнейшее выполнение программы.
При обработке такой ситуации Delphi, как обычно, работает с объектами. С точки зрения компилятора Delphi исключительная ситуация - это объект. Для работы с этим специфичным объектом в Delphi (точнее, в Object Pascal) были введены следующие языковые конструкции: try .. except и try .. finally.
Рассмотрим эти языковые конструкции более подробно.
Итак, конструкция try .. except имеет следующий синтаксис (листинг 1.6):
Листинг 1.6
try
{исполняемый код};
except
on Exceptionl do {код, исполняемый в случае возникновения ошибки 1};
on Exception2 do {код, исполняемый в случае возникновения ошибки 2};
else
{код, обработчик всех не перехваченных ранее ошибок};
end;
Если при выполнении кода, размещенного в разделе try, генерируется исключение, то выполнение этого раздела прекращается и управление передается коду, размещенному в разделе except. Раздел except может использоваться двумя способами. Во-первых, в нем могут располагаться любые операторы, кроме обработчиков исключений, начинающихся с приставки on. Это и операторы сообщения об ошибке, и команды, позволяющие освобождать системные ресурсы, а также другие операторы и команды. Во-вторых, раздел except используется для обработки исключений. В этом случае в него могут включаться только операторы обработки исключений. Если среди обработчиков встретился обработчик, соответствующий сгенерированному исключению, то выполняется оператор этого обработчика, исключение разрушается и управление передается коду, расположенному после оператора on Exception do. Раздел, расположенный после ключевого слова else, служит для обработки любых исключений, не описанных в разделе except. Этот раздел не является обязательным. Если при обработке исключительной ситуации не будет найден подходящий обработчик, то произойдет обработка системным обработчиком исключений.
Рассмотрим простой пример обработки исключительной ситуации деления на ноль (листинг 1.7).
Листинг 1.7
try
а:=10;
b:=0;
c:=a/b;
except
on EZeroDivide do MessageBox('Делить на ноль нельзя!');
end;
Итак, как можно видеть из приведенного выше примера, для обработки разных исключений служат разные операторы. Рассмотрим более подробно оператор обработки on .. do. Данный оператор находится внутри раздела except и может иметь две формы (листинг 1.8).
Листинг 1.8
on <класс исключения> do <оператор>;
или
on <имя>: <класс исключения>
do <операторы, в которых можно использовать свойства исключения>
Этот оператор обрабатывает только тот класс исключений, который в нем указан. При указании родительского (базового) класса, все классы исключений - потомки данного класса - также будут обработаны. Для обработки всех исключений можно обратиться к базовому классу всех исключений: Exception. После обработки исключения оно разрушается. Вторая форма оператора on .. do отличается от первой тем, что данному исключению можно временно присвоить имя и обращаться к свойствам исключения. Обращаться к свойствам исключения можно с помощью конструкции <имя>.<имя свойства>. Посмотрим листинг 1.9.
Листинг 1.9
try
ScrollBarl.Max := ScrollBarl.Min - 1;
except
on E: EInvalidOperation do
MessageDlg( 'Игнорируем исключение: '- + E.Message, mtlnformation, [mbOK], O)
end;
В приведенном примере мы присваиваем исключению EInvalidOperation временное имя Е. Затем в окне сообщения выводим текст ошибки E.Message, выдаваемый Delphi по умолчанию (если бы не было нашего обработчика ошибки).
Примечание
Никогда не уничтожайте временный объект исключения. Обработчик исключений автоматически уничтожает объекты исключения. Если вы уничтожите объект исключения самостоятельно, это может привести к ошибке доступа.
Иногда, бывает необходимо, чтобы после обработки исключительной ситуации своим кодом вызывался стандартный обработчик ошибки. Например, в случае возникновения некоторой ошибки вы хотите, чтобы приложение сообщало пользователю какую-либо информацию, а затем передавало управление стандартному обработчику ошибок. Как вы уже знаете, после обработки исключения вашим кодом, исключение уничтожается. Для того чтобы самостоятельно вызвать снова это исключение, можно воспользоваться регенерацией исключений. Для регенерации исключения служит команда raise. Рассмотрим листинг 1.10.
Листинг 1.10
try
{ операторы }
except
on <класс исключения> do
begin
{операторы обработки исключения}
raise; // Регенерация исключения
end;
end;
После выполнения операторов обработки исключения, написанных программистом, выполняется команда raise, которая снова принудительно вызывает это исключение, после чего управление передается стандартному обработчику исключений.
В случае, когда исключение успешно проходит через все блоки try в коде приложения, вызывается метод HandleException. Он показывает диалоговое окно ошибки. Вы можете вызвать этот метод так, как в листинге 1.11.
Листинг 1.11
try
{ операторы } except
Application.HandieException(Self);
end;
Следующая конструкция try .. finally служит для защиты кода, записанного в разделе finally от исключительных ситуаций, которые в силу каких-либо причин могут происходить в разделе try. Синтаксис этой конструкции представлен на листинге 1.12.
Листинг 1.12
try
{операторы, способные создать исключительную ситуацию};
finally
{защищенные операторы, выполняемые в любом случае};
end;
Итак, операторы, которые размещены после ключевого слова finally, будут выполняться в любом случае, была сгенерирована исключительная ситуация или нет. Если в разделе try была сгенерирована исключительная ситуация, то управление немедленно передается разделу finally. Также, если исключительной ситуации в разделе, try не было, блок finally будет выполняться. Даже если в разделе finally произойдет ошибка, выполнение операторов этого раздела будет продолжено до конца. В конструкции try .. finally не происходит обработка исключений, она используется в основмом для освобождения ресурсов памяти, закрытия ненужных файлов и других операций освобождения ресурсов. Таким образом, в данной конструкции нуждаются операции с файлами, памятью, ресурсами Windows и объектами.
Код обработки исключения можно разбить на блоки try .. except .. end и try .. finally .. end. Эти блоки могут быть вложенными (рис. 1.24, а и б).
При разработке приложений на Delphi часто возникают ситуации, когда программисту не требуется обрабатывать исключения, а необходимо лишь прервать нежелательное действие, вызывающее ошибку. Для этого применяются так называемые молчаливые исключения (silent exceptions). Молчаливые исключения являются потомками стандартного исключения EAbort. По умолчанию обработчик ошибок VCL Delphi отображает на экране диалоговое окно ошибки для всех исключений, кроме наследников EAbort.
Примечание
При создании консольных приложений сведения об ошибке выводятся и для необработанных исключений EAbort.
Для того чтобы сгенерировать молчаливое исключение, можно вызвать процедуру Abort. Она автоматически сгенерирует исключение EAbort, которое прервет текущую операцию без вывода сведения об ошибке на экран. Рассмотрим пример. Пусть форма содержит пустой список (ListBoxi) и кнопку (Button1). Запишем в обработчик события кнопки onclick следующий код:
Листинг 1.13
procedure TForml.ButtonlClick(Sender: TObject);
var
I: Integer;
begin
for I := 1 to 10 do (цикл 10 раз}
begin
ListBoxl.Items.Add(IntToStr(I)); {добавляем номер в список}
if I = 7 then Abort; {прерываем добавление номеров в список после добавления седьмого номера}
end;
end;
В результате работы программы, после нажатия кнопки Button1, в список будет добавлено семь строк с номерами от 1 до 7.
Рис. 1.24. Вложенные блоки в обработчике исключений (а) и в конструкции защиты кода (б)
Обработка RTL-исключений. Иерархия исключений
RTL (real time library)-исключения определены в модуле Delphi sysutils и являются наследниками базового класса исключений Exception.
Имеется несколько типов исключений-наследников RTL (табл. 1.1).
Таблица 1.1. Типы исключений из RTL

| Тип ошибки | Причина | Значение |
| Ввод/вывод | Ошибка доступа к файлу или устройству ввода/вывода | Большинство исключений ввода/вывода связано с кодом ошибки, возвращаемом Windows при обращении к файлу |
| Куча | Ошибка использования динамической памяти | Ошибки кучи возникают при недостатке памяти или когда в приложении присутствует указатель на область памяти вне кучи |
| Целочисленные математические операции | Неправильное действие с выражением целого типа | Ошибки включают в себя: деление на ноль, переполнение, выход за пределы диапазона и др. |
| Математические операции с плавающей точкой | Неправильное действие с выражением вещественного типа | Ошибки с вещественными числами могут исходить от математического сопроцессора или программного эмулятора. Ошибки включают в себя: неправильные инструкции, деление на ноль, переполнение и др. |
| Операция аs | Неправильная работа с классами при помощи операции as | Объекты могут работать только с совместимыми объектами |
| Преобразование | Неправильное преобразование типов | Функции преобразования типов (IntToStr, StrToInt И др.) генерируют эту ошибку в случае невозможности преобразования |
| Аппаратные | Системные условия | Аппаратные ошибки указывают, что или процессор, или пользователь сгенерировал ошибку: доступа, переполнения стека или другую |
| Тип Variant | Неправильное использование типа Variant | Ошибка возникает в выражениях, где не может использоваться тип Variant |

Рассмотрим теперь иерархию классов исключений .более подробно:

| Exception | - базовый класс исключений |
| EAbort | - исключение для намеренного прерывания вычислений |
| EAbstractError | - попытка вызова абстрактного метода |
| EAccessViolation | - ошибка доступа к памяти |
| EArrayError | - ошибка при работе с массивами |
| EAssertionFailed | - ошибка при проверке истинности |
| EBitsError | - ошибка доступа к массиву булевых величин TBits |
| ECacheError | - ошибка построения кэша |
| EComponentError | - ошибка регистрации или переименования компонента |
| EControlC | - нажатие пользователем клавиш <Ctrl>+<C> при выполнении консольного приложения |
| EConvertError | - ошибка преобразования строк (объектов) |
| EDatabaseError | - ошибка работы с базами данных |
| EDBClient | - ошибка в наборе данных клиента |
| EReconcileError | - ошибка обновления данных компонента TClientDataset |
| EDBEngineError | - ошибка в BDE |
| ENoResultSet | - генерируется компонентом TQuery при попытке открыть запрос без select |
| EUpdateError | - ошибка при обновлении В TProvider |
| EDateTimeError | - ошибка ввода даты или времени |
| EDimensionMapError | - ошибка формата данных в кубе решений |
| EDimlndexError | - ошибочный индекс в задании размерности в кубе решений |
| EExternalException | - неизвестное исключение |
| EInOutError | - ошибка ввода/вывода в файл |
| EIntError | - базовый класс исключений целочисленных математических операций |
| EDivByZero | - ошибка деления на ноль |
| ERangeError | - значение или индекс вне допустимого диапазона |
| EIntOverflow | - переполнение |
| EIntfCastError | - ошибочное преобразование типов as к интерфейсу |
| EInvalidGraphic | - нераспознаваемый графический файл |
| EInvalidGraphicOperation | - ошибка при операциях с графикой |
| EInvalidGridOperation | - ошибка при работе с сеткой (Grid) |
| EInvalidOperation | - ошибочная операция с компонентом |
| EInvalidPointer | - ошибка при операциях с указателем |
| EListError | - ошибка при работе со списком |
| ELowCapacityError | - ошибка выделения памяти для куба решений |
| EMathError | - базовый класс исключений операций с плавающей запятой |
| EInvalidArgument | - недопустимое значение параметра при обращении к математической функции |
| EInvalidOp | - неопределенная операция |
| EOverflow | - ошибка переполнения |
| EUnderflow | - потеря значащих разрядов |
| EZeroDi'vide | - ошибка деления на ноль |
| EMCIDeviceError | - ошибка доступа к устройствам через драйвер MCI (Media Control Interface) |
| EMenuError | - ошибка при работе с элементами меню |
| EOleCtrlError | - ошибка при связывании приложения с элементом ActiveX |
| EOleError | - низкоуровневая ошибка OLE |
| EOleSysError | - ошибка интерфейса OLE IDispatch |
| EOleException | - ошибка OLE, связанная со свойством или методом |
| EOutlineError | - ошибка при работе с Tout line |
| EOutOfMemory | - ошибка распределения памяти |
| EOutOfResources | - ошибка создания обработчика Windows |
| EPackageError | - исключение, генерируемое при загрузке или использовании пакета |
| EParserError | - ошибка преобразования текста описания формы в двоичный формат |
| EPrinter | - ошибка печати |
| EPrivelege | - ошибка выполнения инструкции процессора из-за нехватки привилегий |
| EPropReadOnly | - ошибка записи с помощью OLE значения свойства, предназначенного только для чтения |
| EPropWriteOnly | - ошибка чтения с помощью OLE значения свойства, предназначенного только для записи |
| EPropertyError | - ошибка при задании значения свойства |
| ERegistryException | - ошибка при работе с реестром Windows |
| EReportError | - ошибка задания типа сервера (компонент TReport не может соединиться с базой данных) |
| EResNotFound | - ошибка загрузки файла ресурсов (*.DFM или *.RES) во время создания приложения |
| EStackOverflow EStreamError | - переполнение стека - базовый класс исключений ошибок потоков |
| EFCreateError | - ошибка создания файла |
| EFOpenError | - ошибка открытия файла |
| EFilerError | - базовый класс исключений файловых потоков |
| EReadError | - ошибка чтения заданного числа байт |
| EWriteError | - ошибка записи заданного числа байт |
| EclassNotFound | - ошибка связи компонента с приложением |
| Elnvalidlmage | - ошибка чтения файла ресурсов |
| EmethodNotFound | - не найден метод |
| EStringListError | - ошибка доступа к окну списка |
| EThread | - ошибка многопоточного приложения |
| ETreeViewError | - ошибка индекса при работе с TTreeview |
| EVariantError | - ошибка при работе с типом данных variant |
| EWin32Error | - внутренняя ошибка Windows |

Итак, класс Exception является базовым классом всех исключений в Delphi. Все вышеописанные классы являются прямыми или косвенными наследниками класса Exception. При создании собственных новых классов исключений необходимо использовать класс Exception как родительский. Только в этом случае Delphi гарантированно распознает и обработает новый класс как исключение. В свою очередь, класс Exception является прямым наследником базового класса TObject, и наследует все его функции. В отличие от
других классов, классы исключений начинаются не с буквы т, а с буквы Е. При создании собственных классов исключений можно называть их по своему усмотрению, не обязательно начиная с буквы Е (но это считается плохим стилем программирования).
Создание собственных исключений
Для создания собственных типов исключений необходимо знать, как определить тип объекта исключения, а также как вызвать исключение.
Так как исключение является объектом Delphi, то определение нового типа исключения так же nporfro, как определение объекта нового типа. Теоретически возможно вызывать любой объект как объект исключения, но стандартные обработчики исключений работают только с теми объектами, предками которых являются Exception или потомки Exception.
В качестве примера создания собственного типа исключения рассмотрим следующее определение:
type
EMyException = class(Exception);
Теперь, если вы вызовете исключение EMyException, но не напишете обработчик для него, то произойдет вызов стандартного обработчика для Exception. Так как стандартный обработчик для Exception показывает имя вызванного исключения, вы можете увидеть имя вашего нового исключения.
Для вызова созданного исключения используйте команду raise. Для примера рассмотрим типичную задачу проверки введенного пользователем пароля:
type
EPasswordlnvalid = class (Exception);
После определения нового типа исключения EpasswordInwalid вы можете вызвать это исключение в любом месте программы:
if Password <> CorrectPassword then raise EPasswordlnvalidCreate('Введен неправильный пароль');
Вызов созданного исключения производится по его имени.
Интерфейсы и их совместное использование классами
Ключевое слово Delphi interface позволяет создавать и использовать интерфейсы в ваших приложениях. Интерфейсы служат для расширения модели наследования в VCL, позволяя одному классу принадлежать нескольким
интерфейсам, а также нескольким классам - наследникам различных базовых классов использовать один интерфейс. Интерфейсы полезны в тех случаях, когда наборы операций, такие как потоки, используются большим количеством объектов.
Таким образом, интерфейсы - это средства для обеспечения взаимодействия между разными объектами.
Интерфейсы являются фундаментом для технологий компонентной объектной модели (СОМ) и CORBA.
Интерфейсы похожи на классы, которые содержат в себе только абстрактные методы и четкие определения их функциональности. Определение метода интерфейса включает в себя параметры и типы параметров, возвращаемый тип, а также ожидаемое поведение. Методы интерфейса семантически или логически связаны с отражением цели интерфейса. Существует соглашение об интерфейсах, гласящее, что каждый интерфейс должен быть назван в соответствии с задачей, которую он будет выполнять. Например, интерфейс iMalloc предназначен для распределения, освобождения и управления памятью. Аналогично, интерфейс IPersist может использоваться как базовый интерфейс для потомков, каждый из которых определяет специфичные прототипы методов для загрузки и сохранения состояния объектов в память, поток или в файл. Приведем простой пример объявления интерфейса (листинг 1.14):
Листинг 1.14
type
IEdit = interface
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Примечание
Для тех читателей, которые имеют слабое представление о создании компонентов и новых классов, советуем прочитать вторую часть книги, посвященную созданию собственных и модификации существующих компонентов.
Нельзя создать экземпляр интерфейса при помощи интерфейса. Для получения экземпляра интерфейса вам нужно объявить его в классе, содержащем данный интерфейс. Таким образом, нужно определить класс, который содержит необходимый интерфейс в списке своих родителей (листинг 1.15).
Листинг 1.15
TEditor = class(TInterfacedObject, lEdit)
procedure Copy; stdcall;
procedure Cut; stdcall;
procedure Paste; stdcall;
function Undo: Boolean; stdcall;
end;
Как уже было отмечено выше, использование интерфейсов позволяет нескольким классам использовать один интерфейс, игнорируя требование наличия одного базового класса-предка. Следует запомнить, что интерфейс - это тип с управляемым временем жизни, т. е., он автоматически, при инициализации, принимает значение nil, обладает счетчиком ссылок и автоматически уничтожается, при выходе за пределы своей области видимости.
Интерфейс IUnknown
По аналогии с наследованием классов, предком которых является базовый класс TObject, все интерфейсы - это прямые или косвенные наследники интерфейса IUnknown. Этот базовый интерфейс описан в модуле System следующим образом (листинг 1.16):
Листинг 1.16
type
IUnknown = interface
['{ 00000000-0000-0000-С000-000000000046}']
function Querylnterfасе(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;7
end;
Синтаксис описания интерфейса похож на описание класса. Главное отличие заключается в том, что интерфейс может быть связан с глобальным уникальным идентификатором (Global Unique Identifier, GUID).
GUID - это 128-разрядное целое число, которое используется для уникальной идентификации интерфейсов. Так как в 128-ми разрядах можно закодировать большое количество чисел, GUID практически гарантирует глобальную уникальность идентификатора интерфейса. То есть, практически невозможно, чтобы на двух компьютерах GUID совпал. Алгоритм генерации GUID основан на аппаратной части компьютера (частота процессора, номер сетевой карты и т. д.). В результате работы алгоритма, который может быть реализован с помощью функции API CocreateGUID (), получается запись типа TGUID. Эту запись можно определить в виде строки следующего формата:
'{хххххххх-хххх-хххх-хххх-хххххххххххх}'
В дальнейшем, при рассмотрении СОМ, мы увидим, что каждый интерфейс или класс СОМ имеет собственный GUID. Для интерфейсов - это идентификатор интерфейса (Interface ID, IID), а для класса - идентификатор класса (Class ID, CLSID).
Для создания нового GUID в среде Delphi достаточно нажать комбинацию клавиш <Ctrl>+<Shift>+<G> в окне редактора кода.
Итак, интерфейс lunknown поддерживает три метода, которые наследуются всеми интерфейсами:
- QueryInterface() - используется для создания запроса, поддерживается ли данный интерфейс и если ответ положителен, то метод возвращает указатель на него. Для примера, предположим, что имеется некоторый объект object, который поддерживает несколько интерфейсов interface1, interface2 и др. Для получения указателя на интерфейс interface2, объекта Object, вам нужно вызвать метод Interface2.Query Interface О;
- _AddRef () - используется, когда получен указатель на данный интерфейс и вы хотите работать с этим указателем. Метод _AddRef() обязательно должен заканчиваться вызовом метода _Release ();
- _Release () - данный метод применяется для завершения работы с интерфейсом.
Интерфейсы являются фундаментальными элементами таких распределенных объектных моделей, как СОМ и CORBA.
Более подробно интерфейс lunknown мы рассмотрим в третьей части книги, посвященной использованию технологий СОМ и ActiveX.
Класс TlnterfacedObject
В VCL Delphi определен класс TlnterfacedObject, который служит базовым классом для объектов интерфейса. Данный класс определен в модуле Delphi system следующим образом (листинг 1.17):
Листинг 1.17.
type
TlnterfacedObject = class(TObject, IUnknown) private
FRefCount: Integer;
protected
function Querylnterface(const IID: TGUID; out Obj): Integer; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall; public
property RefCount: Integer read FRefCount;
end;
Как мы видим, данный класс в качестве родителей имеет класс TObject и интерфейс lunknown. Класс Tinterfacedobject позволяет достаточно легко создавать классы, поддерживающие интерфейсы. Например,
type
TMyObjInterfaced = class(TInterfacedObject, IPaint)
end;
На вышеприведенном примере мы определяем новый класс TMyObj interfaced, который является прямым потомком класса Tinterfacedobject и поддерживает некий интерфейс IPaint.
Использование оператора as
Объекты, поддерживающие интерфейсы, могут использовать оператор as для динамического присоединения интерфейса. Например,
procedurePaintObjecta(P: TInterfacedObject) var
X: IPaint; begin
X := P as IPaint;
{операторы}
end;
В этом примере переменная Р имеет тип Tinterfacedobject. Данная переменная может быть назначена переменной х, как ссылка на интерфейс IPaint.. Для такого назначения компилятор генерирует код для вызова метода Querylnterface, относяшегося к Интерфейсу IUnknown переменной Р. Подобное назначение возможно, даже если Р не поддерживает данный интерфейс. То есть, компилятор не выдаст ошибку при таком назначении.
Во время выполнения вышеприведенного примера либо успешно происходит присваивание
Х:= Р as IPaint;
либо генерируется исключительная ситуация.
При использовании оператора as вы должны выполнять следующие требования:
- при объявлении интерфейса, явно объявляйте в качестве предка интерфейс lunknown. Так как только в этом случае вы сможете воспользоваться оператором аs;
- если вы используете оператор as для интерфейса, данный интерфейс должен иметь свой IID. Напомним, что для создания нового IID достаточно, находясь в редакторе кода, использовать комбинацию клавиш <Ctrl>+<Shift>+<G>.
Использование ключевого слова implements
Многие классы VCL Delphi имеют в качестве некоторых своих свойств объекты. Кроме того, вы можете использовать в качестве свойств класса интерфейсы. В том случае, когда свойство имеет тип интерфейса, то вы можете использовать ключевое слово implements для определения методов, которые данный интерфейс передает объекту. По умолчанию, ключевое слово implements передает все методы интерфейса. Тем не менее, вы можете самостоятельно определить список тех методов интерфейса, которые передаются объекту.
На приведенном ниже листинге 1.18 представлен пример использования ключевого слова implements при создании объекта адаптера цвета, предназначенного для преобразования восьмибитного значения цвета RGB.
Листинг 1.18
unit cadapt;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
IRGBSbit = interface
['{Id76360a-f4f5-lldl-87d4-00c04fbl7199}']
function Red: Byte;
function Green: Byte;
function Blue: Byte;
end;
IColorRef = interface
[41d76360b-f4f5-lldl-87d4-00c04fbl7199}'] function Color: Integer; end;
TRGBSColorRefAdapter = class(TInterfacedObject, IRGBSbit, IColorRef) private
FRGBSbit: IRGBSbit;
FPalRelative: Boolean; public
constructor Create(rgb: IRGBSbit);
property RGBSIntf: IRGBSbit read FRGBSbit implements IRGBSbit;
property PalRelative: Boolean read FPalRelative write-FPalRelative;
function Color: Integer; end;
implementation
constructor TRGBSColorRefAdapter.Create(rgb: IRGBSbit);
begin
FRGBSbit := rgb; end;
function TRGBSColorRefAdapter.Color: Integer;
begin
if FPalRelative then
Result := PaletteRGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue) else
Result := RGB(RGBSIntf.Red, RGBSIntf.Green, RGBSIntf.Blue);
end;
end.
Использование интерфейсов в распределенных приложениях
Интерфейсы являются фундаментальным элементом распределенных объектных моделей СОМ и CORBA (более подробно о моделях читайте в третьей части книги). Delphi обеспечивает базовые классы для этих технологий, которые расширяют возможности объекта TInterfacedObject.
Классы СОМ добавляют возможности использования фабрик классов и идентификаторов классов (CLSID). Фабрики классов отвечают за создание экземпляров классов посредством CLSID. В свою очередь, CLSID используются для регистрации и манипуляции классами СОМ. Классы СОМ, которые обладают и фабрикой класса, и идентификатором класса, называются CoClasses. CoClasses имеют преимущество перед Querylnterface по части поддержки новых версий интерфейсов. Новые версии старых интерфейсов автоматически становятся доступными для программ-клиентов. В приложе ниях СОМ разработчик может вносить правку в код интерфейса для улучшения работы приложения, не изменяя клиентской части кода.
Другая распределенная технология называется CORBA (Архитектура Брокера Общих Объектных Запросов, Common Object Request Broker Architecture). Описание данной технологии не входит в эту книгу, заметим только, что она позволяет создавать приложения для взаимодействия с различными аппаратными или программными платформами. Так, клиентское приложение CORBA, работающее в операционной системе Windows 98, также легко будет работать с сервером приложений операционной системы UNIX.

Глава 1 Содержание Глава 3