2009-08-14 13 views
12

Robię pełną wersję starej biblioteki i nie jestem pewien, jak sobie z tym poradzić (ze względu na zrozumiałość, wszyscy twierdzą, że chodzi o analogię roweru):Jak zaimplementować dziedziczenie wielokrotne w delphi?

Mam następujące klasy:

  • TBike - rower sam
  • TBikeWheel - jeden z koła roweru
  • TBikeWheelFront i TBikeWheelBack obie dziedziczy TBikeWheel a następnie realizuje konkretne rzeczy, których potrzebują na nim

Jest to dość proste, ale teraz decyduję się na stworzenie wielu rodzajów rowerów, z których każdy ma własny rodzaj koła - robią to samo, co zwykłe koła przednie/tylne, a także specyfikę tego roweru .

  • TBikeXYZ - dziedziczy z TBike
  • TBikeWheelXYZ - dziedziczy TBikeWheel

I tu jest mój problem: TBikeWheelFrontXYZ powinny dziedziczyć TBikeWheelXYZ (aby uzyskać szczegółowe sposoby na kole XYZ), ale powinien również odziedziczyć po TBikeWheelFront (aby uzyskać konkretne metody przedniego koła).

Moje pytanie brzmi, jak mogę wdrożyć to w sposób, który nie:

  1. poczuć się jak hack
  2. siła mnie do przepisywania tego samego kodu kilka razy

Odpowiedz

16

Delphi nie obsługuje wielokrotnego dziedziczenia. Ale klasy mogą obsługiwać/wdrażać wiele interfejsów i można delegować implementację interfejsu, aby można było symulować wielokrotne dziedziczenie.

3

Zasadniczo - NIE MOŻESZ. Delphi nie obsługuje dziedziczenia wielokrotnego.

Pozostawia się ten dylemat, pytanie brzmi: czy możliwe jest zreorganizowanie tej biblioteki w taki sposób, aby można było uniknąć interfejsu? Czy dziedziczenie wielokrotne dotyczy głównie funkcji i metod? Jeśli tak, użyj interfejsów. Delphi może obsługiwać wiele interfejsów w klasie.

Jeśli dziedziczenie wielu dziedziczek dotyczy raczej dziedziczenia rzeczywistej funkcjonalności na zajęciach, prawdopodobnie obawiam się, że warto rozważyć refaktoryzację na większą skalę. Musisz znaleźć sposób na rozbicie zależności funkcjonalnych w taki sposób, aby można było dziedziczyć po jednej klasie bazowej, prawdopodobnie z dodatkowymi interfejsami, które zostały wrzucone.

Niestety, nie mogę podać łatwej odpowiedzi - to tylko rzeczywistość.

Marc

2

Możesz spróbować wyodrębnić interfejs, powiedz IFrontWheel, z TBikeWheelFront tak, aby był podklasą TBikeWheel, ale implementuje IFrontWheel. Następnie TBikeWheelXYZ dziedziczy po TBikeWheel i TBikeWheelFrontXYZ dziedziczy po TBikeWheelXYZ i implementuje IFrontWheel.

Następnie można zdefiniować klasę TFrontwheel i nadać jej te same metody co interfejs, ale teraz można je zaimplementować. Następnie TBikeWheelFront i TBikeWheelXYZ otrzymują prywatnego członka typu TFrontwheel i implementacje IFrontWheel po prostu przekazują je do prywatnych metod członkowskich.

W ten sposób nie ma podwójnych implementacji.

13

Użyj interfejsów. Coś takiego (Off szczycie głowy, oparty na opisie .....)

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); 

Następnie realizować zajęcia dla interfejsów że robić to, co odpowiednie zajęcia w starym (przypuszczalnie C/C++), biblioteka robi i tworzy ich instancje w odpowiednim konstruktorze klasy.

4

Użyj polimorfizmu, aby zaimplementować każdą "rzecz" jako hierarchię obiektów, a następnie dodaj właściwości obiektu do tego obiektu po kolei. Stwórz hierarchię kół i hierarchię rowerów. Następnie dodaj koła do rowerów jako pola w obiekcie rowerowym przodka. Zobacz poniżej.

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; 
+0

+1, powstrzymywanie często modeluje rzeczywistość znacznie lepiej niż dziedziczenie. – mghie

1

Odmianą sugestią Brian Frost:

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; 
1

Inną alternatywą z nowszymi wersjami Delphi jest wykorzystanie leków generycznych w kompozycyjnej modelu. Jest to szczególnie przydatne w przypadku, gdy wiele klas bazowych (TBarA i TBarB w tym przykładzie) nie jest dostępnych do modyfikacji (tj. Klasy szkieletowe lub biblioteki). Na przykład (uwaga, konieczne destructor w TFoo<T> jest pominięta tutaj dla zwięzłość):

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. 
0

można spróbować w ten sposób, jeśli nie chcesz, aby powtórzyć kod kilka razy i chcę kod oddzielonej od produkcji.

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; 
0

Niestety, Delphi nie obsługuje wielu dziedziczeń.

Powiązane problemy