Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi.pdf
Скачиваний:
197
Добавлен:
24.02.2016
Размер:
6.84 Mб
Скачать

Но это еще не все. Мы совсем забыли о методах QueryInterface, _AddRef и _Release, которые тоже должны быть реализованы. К счастью, вам нет необходимости ломать голову над реализацией этих методов, поскольку разработчики системы Delphi уже позаботились об этом. Стандартная реализация методов интерфейса IInterface находится в классе TInterfacedObject. Мы его рассмотрим ниже, а сейчас просто унаследуем класс TTextReader от класса TInterfacedObject — и он получит готовую реализацию методов QueryInterface, _AddRef и _Release.

type

TTextReader = class(TInterfacedObject, ITextReader)

...

end;

Теперь реализация интерфейса ITextReader полностью завершена и можно переходить к использованию объектов класса TTextReader через этот интерфейс.

6.6. Использование интерфейса

Для доступа к объекту через интерфейс нужна интерфейсная переменная:

var

Intf: ITextReader;

Интерфейсная переменная занимает в оперативной памяти четыре байта, хранит ссылку на интерфейс объекта и автоматически инициализируется значением nil.

Перед использованием интерфейсную переменную инициализируют значением объектной переменной:

var

Obj: TTextReader; // объектная переменная Intf: ITextReader; // интерфейсная переменная

begin

...

Intf := Obj;

...

end;

После инициализации интерфейсную переменную Intf можно использовать для вызова методов объекта Obj:

Intf.Active :=

True; //

->

Obj.SetActive(True);

Intf.NextLine;

//

->

Obj.NextLine;

247

Через интерфейсную переменную доступны только те методы и свойства объекта, которые есть в интерфейсе:

Intf.Free; // Ошибка! У интерфейса ITextReadaer нет метода Free. Obj.Free; // Метод Free можно вызвать только так.

6.7. Реализация нескольких интерфейсов

Один класс может содержать реализацию нескольких интерфейсов. Такая возможность позволяет воплотить в классе несколько понятий. Например, класс TTextReader — "считыватель табличных данных" — может выступить еще в одной роли — "считыватель строк". Для этого он должен реализовать интерфейс IStringIterator:

type

IStringIterator = interface function Next: string; function Finished: Boolean;

end;

Интерфейс IStringIterator предназначен для последовательного доступа к списку строк. Метод Next возвращает очередную строку из списка, метод Finished проверяет, достигнут ли конец списка.

Реализуем интерфейс IStringIterator в классе TTextReader таким образом, чтобы последовательно считывались значения из ячеек таблицы. Например, представьте, что в некотором файле дана таблица:

Aaa Bbb Ccc

Ddd Eee Fff

Ggg Hhh Iii

Чтение этой таблицы через интерфейс IStringIterator вернет следующую последовательность строк:

Aaa

Bbb

Ccc

Ddd

Eee

Fff

Ggg

Hhh

Iii

248

Ниже приведен программный код, обеспечивающий поддержку интерфейса

IStringIterator в классе TTextReader:

type

TTextReader = class(TInterfacedObject, ITextReader, IStringIterator) FColumnIndex: Integer;

function Next: string; function Finished: Boolean;

...

end;

...

function TTextReader.Next: string; begin

if FColumnIndex = ItemCount then // Если пройден последний элемент

текущей строки,

// то переходим к следующей строке

begin

таблицы

 

NextLine;

 

FColumnIndex := 0;

 

end;

 

Result := Items[FColumnIndex]; FColumnIndex := FColumnIndex + 1;

end;

function TTextReader.Finished: string; begin

Result := EndOfFile and (FColumnIndex = ItemCount); end;

Теперь объекты класса TTextReader совместимы сразу с тремя типами данных: TInterfacedObject, ITextReader, IStringIterator.

var

Obj: TTextReader; Reader: ITextReader;

Iterator: IStringIterator; begin

...

Reader := Obj; // Правильно Iterator := Obj; // Правильно

...

end;

В одном случае объект класса TTextReader рассматривается как считыватель табличных данных, а в другом случае — как обычный список строк с последовательным доступом. Например, если есть две процедуры:

procedure LoadTable(Reader: ITextReader); procedure LoadStrings(Iterator: IStringIterator);

249

то объект класса TTextReader можно передать в обе процедуры:

LoadTable(Obj);

//

Obj

воспринимается

как

ITextReader

LoadStrings(Obj); //

Obj

воспринимается

как

IStringIterator

6.8. Реализация интерфейса несколькими классами

Несколько совершенно разных классов могут содержать реализацию одного и того же интерфейса. С объектами таких классов можно работать так, будто у них есть общий базовый класс. Интерфейс выступает аналогом общего базового класса.

Рассмотрим пример. Представьте, что есть два класса: TTextReader и TIteratableStringList:

type

TTextReader = class(TInterfacedObject, ITextReader, IStringIterator)

...

end;

TIteratableStringList = class(TStringList, IStringIterator)

...

end;

Схематично полученную иерархию классов можно представить так (рисунок

6.2):

Рисунок 6.2. Иерархия классов, реализующих интерфейсы. Сплошными линиями отмечено наследование классов, а пунктирными линиями — реализация интерфейсов классами.

Объекты классов TTextReader и TIteratableStringList несовместимы между собой. Тем не менее, они совместимы с переменными типа IStringIterator. Это значит, что если есть процедура:

procedure LoadStrings(Iterator: IStringIterator);

то вы можете передавать ей объекты обоих упомянутых классов в качестве аргумента:

250

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]