Наследование интерфейса без универсалий


Я пытаюсь реализовать интерфейс для преобразования записей в наборе данных в записи Delphi в предгенерируемой версии Delphi. Мне не нравится интерфейс в данный момент, так как он всегда будет нуждаться в вызовах поддержки, которых я хотел бы избежать, если это возможно, и задавался вопросом, есть ли лучший способ сделать это, который я упускаю.

Пока у меня есть интерфейс навигации и интерфейс поиска данных, определенный:

IBaseRecordCollection = interface
  procedure First;
  procedure Next;
  function BOF: boolean;
  ... // other dataset nav stuff
end;

IRecARecordCollection = interface
  function GetRec: TRecA;
end;

IRecBRecordCollection = interface
  function GetRec: TRecB;
end;

В основном у меня есть конкретный базовый класс, который содержит частный dataset и реализует IBaseRecordCollection и конкретный класс для каждого интерфейса RecordCollection, который является производным от абстрактного класса, реализующего IBaseRecordCollection (обрабатывается свойством implements) с реализацией процедуры извлечения записей:

TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  constructor Create(aRecordCollection: IBaseRecordCollection);
end;

TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRecARecordCollection);
public
  function GetRec: TRecA;
end;
Теперь, чтобы использовать это, я вынужден иметь строитель, который возвращает IRecARecordCollection, а затем возиться с Supports, что мне не нравится, так как он всегда будет использоваться таким образом.

То есть

procedure GetMyRecASet;
var
  lRecARecordCollection: IRecARecordCollection;
  lRecordCollection: IBaseRecordCollection;
begin
  lRecARecordCollection := BuildRecACollection;
  if not supports(lRecARecordCollection, IBaseRecordCollection, lRecordCollection) then 
     raise exception.create();
  while not lRecordCollection.EOF do
  begin
    lRecARecordCollection.GetRec.DoStuff;
    lRecordCollection.Next;
  end;
end;

Хотя это и работает, я не в восторге от supports вызов и смешивание моих lRecordCollections и моих lRecARecordCollections, как это. Первоначально я надеялся, что смогу сделать что-то вроде:

IBaseRecordCollection = interface
  // DBNav stuff
end;

IRecARecordCollection = interface (IBaseRecordCollection)
  function GetRec: TRecA;
end;

TRec1RecordCollection = class(TInterfacedObject, IRecARecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  function GetRec: TRecA;
end;
Но, к сожалению, Delphi не был достаточно умен, чтобы понять, что реализация IRecARecordCollection была разделена на базовую IBaseRecordCollection в вызове свойства коллекции implements и объект TRec1RecordCollection. Есть ли какие-либо другие предложения относительно более аккуратных способов достижения этого?

-- редактировать, чтобы дать (более длинный) ответ к ответу @ David, чем это возможно в комментарии

Предлагаемое решение задачи:

IBaseRecordCollection = interface ['{C910BD0A-26F4-4682-BC82-605C4C8F9173}']
  function GetRecNo: integer;
  function GetRecCount: integer;
  function GetFieldList: TFieldList;
  function EOF: boolean;
  function BOF: boolean;
  ...
end;

IRec1RecordCollection = interface (IBaseRecordCollection) ['{E12F9F6D-6D57-4C7D-AB87-8DD50D35DCA2}']
  function GetRec: TRec1;
  property Rec: TRec1 read GetRec;
end;

TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
  FCollection: IBaseRecordCollection;
protected
  property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
  constructor Create(aRecordCollection: IBaseRecordCollection);
end;

TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRec1RecordCollection, IBaseRecordCollection)
private
  function GetRec: TRec1;
public
  property Rec: TRec1 read GetRec;
end;

Не компилируется. Он жалуется, что TRec1RecordCollection не может найти методы, связанные с IBaseRecordCollection. Я также попытался переместить свойство Collection из абстрактного в Rec1RecordCollection и повторно объявить свойство в TRec1RecordCollection с тем же результатом

Глядя немного глубже, кажется, что прямое наследование класса, реализующего IBaseRecordCollection, будет работать, но Delphi не может справиться с этим косвенно через свойство, использующее implements.

1 5

1 ответ:

Ваш код почти там. Директива implements в коде не компилируется, поскольку вы только объявили, что ваш класс реализует производный интерфейс. В данном случае ваш класс не реализует интерфейс, на который ссылается директива implements, а именно IBaseRecordCollection. Вы можете подумать, что это будет выведено из наследства, но это не так.

Чтобы решить вашу проблему, вам просто нужно объявить, что TRec1RecordCollection реализует оба интерфейса:

type
  TRec1RecordCollection = class(TInterfacedObject, IBaseRecordCollection, 
    IRecARecordCollection)
  ....
  end;

Сделайте только один маленький измените, и ваш код будет компилироваться.

Обновить

Ваша правка вопроса несколько меняет это. Код в моем ответе действительно компилируется, учитывая код в вашем исходном вопросе. Однако добавьте любой метод в IBaseRecordCollection, и компиляция не примет его.

Компилятор должен принять этот код, и тот факт, что он этого не делает, вызван ошибкой компилятора. Современные версии Delphi примут код в вашем обновлении к вопросу.

Если вы не обновите ваш компилятор вы не сможете сделать вашу запланированную работу дизайна.