Как реализовать множественное наследование в delphi?
я делаю полную переписку старой библиотеки, и я не уверен, как справиться с этой ситуацией (ради понимания, все приветствуют аналогию с велосипедом):
у меня есть следующие классы:
-
TBike
- сам велосипед -
TBikeWheel
- одно из колес велосипеда -
TBikeWheelFront
иTBikeWheelBack
, оба наследуются отTBikeWheel
а затем реализует конкретный материал, который им нужен поверх него
это довольно просто, но теперь я решил создать несколько видов велосипедов, каждый из которых имеет свои собственные виды колес - они делают то же самое, что и обычные передние/задние колеса, а также конкретные для этого велосипеда.
-
TBikeXYZ
- наследует отTBike
-
TBikeWheelXYZ
- наследует отTBikeWheel
и вот моя проблема: TBikeWheelFrontXYZ
должны наследовать от TBikeWheelXYZ
(чтобы получить конкретные методы колеса XYZ), но он также должен наследовать от TBikeWheelFront
(чтобы получить конкретные методы переднего колеса).
мой вопрос здесь в том, как я могу реализовать это таким образом, который не делает:
- почувствуйте себя взломщиком
- заставьте меня переписать один и тот же код несколько раз
9 ответов
Delphi не поддерживает множественное наследование. Но классы могут поддерживать / реализовывать несколько интерфейсов, и вы можете делегировать реализацию интерфейса, поэтому вы можете имитировать множественное наследование.
использовать интерфейсы. Что-то вроде этого (с моей головы, основываясь на вашем описании.....)
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++) библиотеке, и создайте их экземпляр в конструкторе соответствующего класса.
используйте полиморфизм, чтобы имплицировать каждую "вещь" как иерархию объектов в своем собственном праве, а затем добавить свойства объекта к этому объекту в свою очередь. Итак, создайте иерархию колес и иерархию велосипедов. Затем добавьте колеса к велосипедам в качестве полей в объекте ancestor bike. Увидеть ниже.
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;
в основном-вы не можете. Delphi не поддерживает множественное наследование.
Итак, оставим эту дилемму, вопрос: не могли бы вы рефакторировать эту библиотеку таким образом, чтобы вы могли уйти с помощью интерфейса? Является ли множественное наследование в основном функциями и методами? Если да-используйте интерфейсы. Delphi может поддерживать несколько интерфейсов в классе.
Если мульти-наследование больше о наследовании фактической функциональности в классах, то вы боюсь, что, возможно, речь идет о более масштабном рефакторинге. Вам нужно будет найти способ разбить эти функциональные зависимости таким образом, чтобы вы могли сделать его наследуемым от одного базового класса, возможно, с некоторыми дополнительными интерфейсами.
к сожалению я не могу дать простой ответ - это просто реальность.
Марк
вы можете попытаться извлечь интерфейс, скажем IFrontWheel, из TBikeWheelFront, так что это подкласс TBikeWheel, но реализует IFrontWheel. Затем TBikeWheelXYZ наследует от TBikeWheel, а TBikeWheelFrontXYZ наследует от TBikeWheelXYZ и реализует IFrontWheel.
затем вы можете определить класс TFrontwheel и дать ему те же методы, что и интерфейс, но теперь вы их реализуете. Затем TBikeWheelFront и TBikeWheelXYZ получают частный член типа TFrontwheel и Реализации IFrontWheel из них просто делегируют частным методам-членам.
таким образом, у вас нет двойных реализаций.
вариант предложения Брайана Фроста:
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;
Другой альтернативой с более новыми версиями Delphi является использование дженериков в композиционной модели. Это особенно полезно в случае, когда несколько базовых классов (TBarA
и TBarB
в этом примере) недоступны для модификации (то есть: framework или классы библиотек). Например (обратите внимание, необходимо 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.
вы можете попробовать этот способ, если вы не хотите повторить код несколько раз и хочу отделить код.
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;