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

Один класс может содержать реализацию нескольких интерфейсов. Такая возможность позволяет воплотить в классе несколько понятий. Например, класс 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

Ниже приведен программный код, обеспечивающий поддержку интерфейса 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);

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

LoadTable(Obj);   // Obj воспринимается как ITextReader
LoadStrings(Obj); // Obj воспринимается как IStringIterator

 

Яндекс цитирования