2009-08-14 18 views
12

Estoy haciendo una reescritura completa de una biblioteca antigua, y no estoy seguro de cómo manejar esta situación (por el bien de ser entendido, toda la analogía bicicleta gran angular):¿Cómo implementar herencia múltiple en delphi?

Tengo las siguientes clases:

  • TBike - la moto en sí
  • TBikeWheel - una de las ruedas de la bicicleta
  • TBikeWheelFront y TBikeWheelBack, ambos hereda de TBikeWheel y luego implementa la materia específica que necesitan en la parte superior de la misma

Esto es bastante sencillo, pero ahora decido crear varios tipos de bicicletas, cada una con sus propios tipos de ruedas: hacen lo mismo que las ruedas delanteras/traseras normales, más lo específico para esa bicicleta .

  • TBikeXYZ - hereda de TBike
  • TBikeWheelXYZ - hereda de TBikeWheel

Y aquí es mi problema: TBikeWheelFrontXYZ debe heredar de TBikeWheelXYZ (para obtener los métodos específicos de una rueda XYZ), pero también debe heredar de TBikeWheelFront (para obtener los métodos específicos de una rueda delantera).

Mi pregunta aquí es, ¿cómo puedo aplicar eso de una manera que no lo hace:

  1. siento como un truco
  2. me obligan a volver a escribir el mismo código varias veces

Respuesta

16

Delphi no soporta herencia múltiple. Pero las clases pueden admitir/implementar múltiples interfaces y usted puede delegar la implementación de la interfaz, por lo que puede simular una herencia múltiple.

3

Básicamente, NO PUEDES. Delphi no admite herencia múltiple.

Así que, con ese dilema, la pregunta es: ¿podría posiblemente refactorizar esa biblioteca de forma que pueda salirse con la suya utilizando la interfaz? ¿La herencia múltiple se trata principalmente de funciones y métodos? Si es así, usa interfaces. Delphi puede soportar múltiples interfaces en una clase.

Si la herencia múltiple es más sobre la herencia de la funcionalidad real de las clases, me temo que probablemente esté buscando una refactorización a mayor escala. Necesitará encontrar una manera de romper esas dependencias funcionales de forma tal que pueda hacer que herede de una sola clase base, posiblemente con algunas interfaces adicionales incluidas.

Lo siento, no puedo proporcionar una respuesta fácil - Esa es solo la realidad de eso.

Marc

2

Puede intentar extraer una interfaz, digamos IFrontWheel, de TBikeWheelFront, de modo que sea una subclase de TBikeWheel pero implemente IFrontWheel. Entonces TBikeWheelXYZ hereda de TBikeWheel y TBikeWheelFrontXYZ hereda de TBikeWheelXYZ e implementa IFrontWheel.

Luego puede definir una clase TFrontwheel y darle los mismos métodos que la interfaz, pero ahora las implementa. Entonces TBikeWheelFront y TBikeWheelXYZ obtienen un miembro privado de tipo TFrontwheel y las implementaciones de IFrontWheel simplemente delegan en los métodos de miembros privados.

De esta manera no tiene implementaciones dobles.

13

Usar interfaces. Algo como esto (De la parte superior de mi cabeza, sobre la base de su descripción .....)

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

luego implementar clases para las interfaces que hacer lo que las clases correspondientes en su edad (presumiblemente C/C++) de la biblioteca hace y crear instancias en el constructor de la clase correspondiente.

4

Use polimorfismo para aplicar cada 'cosa' como una jerarquía de objetos por derecho propio y luego agregue propiedades de objeto a ese objeto sucesivamente. Entonces, crea una jerarquía de ruedas y una jerarquía de bicicletas. A continuación, agregue ruedas a las bicicletas como campos en el objeto de la bicicleta ancestro. Vea abajo.

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, la contención a menudo modela la realidad mucho mejor que la herencia. – mghie

1

Una variación de la sugerencia de Brian heladas:

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

Otra alternativa con las nuevas versiones de Delphi es aprovechar los genéricos en un modelo de composición. Esto es particularmente útil en el caso donde las múltiples clases base (TBarA y TBarB en este ejemplo) no son accesibles para su modificación (es decir, marco o clases de biblioteca). Por ejemplo (nota, la necesaria destructor en TFoo<T> se omite aquí por razones de brevedad):

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

puede probar de esta manera, si usted no desea repetir el código varias veces y desea un código desacoplado.

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

Lo sentimos, Delphi no es compatible con Multiple Herencia.