Как реализовать множественное наследование в Delphi?

Я полностью переписываю старую библиотеку и не знаю, как справиться с этой ситуацией (ради понимания, приветствую аналогию с велосипедом):

У меня есть следующие классы:

  • TBike - сам велосипед
  • TBikeWheel - одно из колес велосипеда
  • TBikeWheelFront и TBikeWheelBack, оба наследуются от TBikeWheel, а затем реализуют конкретные вещи, которые им нужны, поверх него.

Это довольно просто, но теперь я решил создать несколько типов велосипедов, каждый из которых имеет свои собственные колеса - они делают то же самое, что и обычные передние / задние колеса, плюс специфичные для этого велосипеда.

  • TBikeXYZ - наследуется от TBike
  • TBikeWheelXYZ - наследуется от TBikeWheel

И вот моя проблема: TBikeWheelFrontXYZ должен наследовать от TBikeWheelXYZ (чтобы получить конкретные методы колеса XYZ), но он также должен наследоваться от TBikeWheelFront (чтобы получить конкретные методы переднего колеса).

Мой вопрос здесь в том, как я могу реализовать это так, как это не так:

  1. чувствую себя хакером
  2. заставить меня переписать один и тот же код несколько раз

person Lepidosteus    schedule 14.08.2009    source источник


Ответы (10)


Delphi не поддерживает множественное наследование. Но классы могут поддерживать/реализовывать несколько интерфейсов, и вы можете делегировать реализацию интерфейса, так что вы можете как бы имитировать множественное наследование.

person Tim Jarvis    schedule 14.08.2009

Используйте интерфейсы. Что-то вроде этого (в голове не укладывается, судя по вашему описанию...)

type

  IBikeWheel = interface
    ...
  end;

  IXYZ = interface
    ...
  end;

  IFrontWheel = interface(IBikeWheel)
    ...
  end;


  TBike = class
    ...
  end;

  TBikeWheel = class(TObject, IBikeWheel);

  TBikeWheelXYZ = class(TBikeWheel, IXYZ);

  TBikeFrontWheelXYZ = class(TBikeWheelXYZ, IFrontWheel);

Затем реализуйте классы для интерфейсов, которые делают то, что делают соответствующие классы в вашей старой (предположительно C/C++) библиотеке, и создайте их экземпляры в конструкторе соответствующего класса.

person Nick Hodges    schedule 14.08.2009

Используйте полиморфизм, чтобы реализовать каждую «вещь» как иерархию объектов в своем собственном праве, а затем по очереди добавляйте свойства объекта к этому объекту. Итак, создайте иерархию колес и иерархию велосипедов. Затем добавьте колеса к велосипедам в виде полей в объекте велосипеда-предка. Увидеть ниже.

  TBikeWheel = class
  TBikeWheelXYZ = class( TBikeWheel ) 

  TBike = class
    FFrontWheel : TBikeWheel;
    property FrontWheel : TBikeWheel
      read FrontWhell  

  TBikeABC = class( TBike)
    constructor Create;
  end;

  constructor TBikeABC.Create;
  begin
    inherited;
    FFrontWheel := TBikeWheel.Create;
  end;

  TBikeXYZ = class( TBike)
    constructor Create;
  end;

  constructor TBikeXYZ.Create;
  begin
    inherited;
    FFrontWheel := TBikeWheelXYZ.Create;
  end;
person Brian Frost    schedule 14.08.2009
comment
+1, сдерживание часто моделирует реальность намного лучше, чем наследование. - person mghie; 14.08.2009

Вариант предложения Брайана Фроста:

  TBikeWheel = class
  TBikeWheelXYZ = class( TBikeWheel ) 

  TBike = class
    FFrontWheel : TBikeWheel;
  protected
    function CreateWheel: TBikeWheel; virtual;
  public
    property FrontWheel : TBikeWheel
      read FrontWheel  
  end;

  TBikeABC = class( TBike)
  protected
    function CreateWheel: TBikeWheel; override;
  end;

  function TBikeABC.CreateWheel: TBikeWheel;
  begin
    result := TBikeWheel.Create;
  end;

  TBikeXYZ = class( TBike)
  protected
    function CreateWheel: TBikeWheel; override;
  end;

  function TBikeXYZ.CreateWheel: TBikeWheel;
  begin
    result := TBikeWheelXYZ.Create;
  end;
person Uwe Raabe    schedule 14.08.2009

В общем - НЕ МОЖЕТЕ. Delphi не поддерживает множественное наследование.

Итак, оставшись с этой дилеммой, возникает вопрос: не могли бы вы реорганизовать эту библиотеку таким образом, чтобы можно было обойтись без использования интерфейса? Является ли множественное наследование главным образом функциями и методами? Если да - используйте интерфейсы. Delphi может поддерживать несколько интерфейсов в классе.

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

Извините, я не могу дать простой ответ - это просто реальность.

Марк

person marc_s    schedule 14.08.2009

Вы можете попытаться извлечь интерфейс, скажем, IFrontWheel, из TBikeWheelFront, чтобы он был подклассом TBikeWheel, но реализовывал IFrontWheel. Затем TBikeWheelXYZ наследуется от TBikeWheel, а TBikeWheelFrontXYZ наследуется от TBikeWheelXYZ и реализует IFrontWheel.

Затем вы можете определить класс TFrontwheel и дать ему те же методы, что и интерфейс, но теперь вы их реализуете. Затем TBikeWheelFront и TBikeWheelXYZ получают частный член типа TFrontwheel, а их реализации IFrontWheel просто делегируют методы частного члена.

Таким образом, у вас нет двойных реализаций.

person Ozan    schedule 14.08.2009

Еще одна альтернатива более новым версиям Delphi — использование дженериков в композиционной модели. Это особенно полезно в случае, когда несколько базовых классов (в данном примере TBarA и TBarB) недоступны для модификации (например, классы фреймворка или библиотеки). Например (обратите внимание, необходимые destructor в TFoo<T> здесь для краткости опущены):

program Project1;

uses SysUtils;

{$APPTYPE CONSOLE}

type    
  TFooAncestor  = class
    procedure HiThere; virtual; abstract;
  end;
  TBarA = class(TFooAncestor)
    procedure HiThere; override;
  end;
  TBarB = class(TFooAncestor)
    procedure HiThere; override;
  end;
  TFoo<T: TFooAncestor, constructor> = class
    private
      FFooAncestor: T;
    public
      constructor Create;
      property SomeBar : T read FFooAncestor write FFooAncestor;
  end;

procedure TBarA.HiThere;
begin
  WriteLn('Hi from A');
end;

procedure TBarB.HiThere;
begin
  WriteLn('Hi from B');
end;

constructor TFoo<T>.Create;
begin
  inherited;
  FFooAncestor := T.Create;
end;

var
  FooA : TFoo<TBarA>;
  FooB : TFoo<TBarB>;
begin
  FooA := TFoo<TBarA>.Create;
  FooB := TFoo<TBarB>.Create;
  FooA.SomeBar.HiThere;
  FooB.SomeBar.HiThere;
  ReadLn;
end.
person J...    schedule 06.08.2014

вы можете попробовать этот способ, если вы не хотите повторять код несколько раз и хотите получить несвязанный код.

type
   TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
   private
      { Private declarations }
   public
      { Public declarations }
   end;

   TBike = class
   end;

   IBikeWheel = interface
      procedure DoBikeWheel;
   end;

   TBikeWheel = class(TInterfacedObject, IBikeWheel)
   public
      procedure DoBikeWheel;
   end;

   IBikeWheelFront = interface
      procedure DoBikeWheelFront;
   end;

   TBikeWheelFront = class(TInterfacedObject, IBikeWheelFront)
   public
      procedure DoBikeWheelFront;
   end;

   IBikeWheelBack = interface
   end;

   TBikeWheelBack = class(TInterfacedObject, IBikeWheelBack)
   end;

   TBikeWheelFrontXYZ = class(TInterfacedObject, IBikeWheel, IBikeWheelFront)
   private
      FIBikeWheel: IBikeWheel;
      FBikeWheelFront: IBikeWheelFront;
   public
      constructor Create();
      property BikeWheel: IBikeWheel read FIBikeWheel implements IBikeWheel;
      property BikeWheelFront: IBikeWheelFront read FBikeWheelFront implements IBikeWheelFront;
   end;

var
   Form1: TForm1;

implementation

{$R *.DFM}

{ TBikeWheel }

procedure TBikeWheel.DoBikeWheel;
begin
   ShowMessage('TBikeWheel.DoBikeWheel');
end;

{ TBikeWheelFrontXYZ }

constructor TBikeWheelFrontXYZ.Create;
begin
   inherited Create;
   Self.FIBikeWheel := TBikeWheel.Create;
   Self.FBikeWheelFront := TBikeWheelFront.Create;
end;

{ TBikeWheelFront }

procedure TBikeWheelFront.DoBikeWheelFront;
begin
   ShowMessage('TBikeWheelFront.DoBikeWheelFront');
end;

procedure TForm1.btnTestClick(Sender: TObject);
var
   bikeWhell: TBikeWheelFrontXYZ;
begin
   bikeWhell := nil;
   try
      try
         bikeWhell := TBikeWheelFrontXYZ.Create;
         IBikeWheelFront(bikeWhell).DoBikeWheelFront;
         IBikeWheel(bikeWhell).DoBikeWheel;
      except
         on E: Exception do
         begin
            raise;
         end;
      end;
   finally
      if Assigned(bikeWhell) then FreeAndNil(bikeWhell);
   end;                                          
end;
person Passella    schedule 06.08.2014

К сожалению, Delphi не поддерживает множественное наследование.

person Dauezevy    schedule 20.03.2015

Я хотел бы предложить следующие шаги:

  1. Наследуйте класс TBikeWheelFrontXYZ либо от TBikeWheelXYZ, либо от TBikeWheelFront (поскольку в Delphi множественное наследование невозможно, как упоминалось в ответах выше).

  2. Преобразуйте один из родительских классов TBikeWheelXYZ или TBikeWheelFront в вспомогательный класс для класса TBikeWheel.

  3. Добавьте вспомогательный модуль класса в модуль, где объявлен класс TBikeWheelFrontXYZ.

person Jacek Krawczyk    schedule 01.03.2021