POO : problème d'héritage

Résolu
Signaler
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005
-
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005
-
Bonjour,



Dans un programme en Delphi6, j'ai le souci suivant :

une classe A contient un objet de type Classe B ; dans cette classe B, je crée un objet de type Classe A.

Maintenant, je fais dériver la classe A en A'. Est-il possible que lors
de l'appel de la méthode de la classe B depuis A' ce soit un objet de
type Classe A' qui soit créé ?



Merci de votre aide.



Thierry

9 réponses

Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Il n'y a pas de "fonction magique" et, à mon humble avis, la seule solution consiste à utiliser une référence de classe (également appelée métaclasse).
Soit l'unité Unit2 modifiée :
unit Unit2;

interface

uses
  Classes;

type
//Déclaration forward
  TClassB =   class ;

  //Déclaration d'une métaclasse
  TClassAClass  = classof TClassA;

  TClassA =   class (TObject)
  private
    FClassB: TClassB;
  public
constructor Create;overload;
    //Constructeur de copie
constructor Create(A: TClassA; AClass: TClassAClass);overload;
    destructor Destroy;override;
    procedure Display(Lines: TStrings);virtual;
  end;

  

  TClassB  = class(TObject)
  private
    FClassA: TClassA;
    FClassAA: TClassA;
  public
constructor Create(Aref: TClassA; AClass: TClassAClass);
    procedure Display(Lines: TStrings);
  end;

  TClassA2 =   class (TClassA)
  public
procedure Display(Lines: TStrings);override;
  end;

implementation

{ TClassA }

constructor TClassA.Create;
begin
inherited;
  FClassB : = TClassB.Create(Self, TClassAClass(Self.ClassType));
end;

constructor TClassA.Create(A: TClassA; AClass: TClassAClass);
begin
inherited Create;
  FClassB :=  A.FClassB;
 end ;

destructor TClassA.Destroy;
begin
  FClassB.Free;
  inherited;
end;

procedure TClassA.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A  => ' + Self.ClassName);
  Lines.Append('FClassB = > ' + FClassB.ClassName);
 end ;

{ TClassB }

constructor TClassB.Create(Aref: TClassA; AClass: TClassAClass);
begin
inherited Create;
  FClassA : = ARef;
  //Utilisation du constructeur de copie
  FClassAA :=  AClass.Create(Aref, AClass);
 end ;

procedure TClassB.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('B  => ' + Self.ClassName);
  Lines.Append('FClassA = > ' + FClassA.ClassName);
  Lines.Append('FClassAA => ' + FClassAA.ClassName);
  //FClassA.Display(Lines);
 end ;

{ TClassA2 }

procedure TClassA2.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A2  => ' + Self.ClassName);
  Lines.Append('FClassB = > ' + FClassB.ClassName);
 end ;

end.

NB : j'ai mis en rouge sombre les lignes ajoutées ou modifiées.

Dans la fiche de démo, le code doit aussi être modifié :
procedure TForm1.Button1Click(Sender: TObject);
var
  ClassA : TClassA;
  ClassB : TClassB;
  ClassA2 :  TClassA2;
begin
  Memo1.Clear;

  ClassA : = TClassA.Create;
  ClassA.Display(Memo1.Lines);

  ClassA2 :=  TClassA2.Create;
  ClassA2.Display(Memo1.Lines);

  //Les 2 membres seront du type TClassA2
  ClassB := TClassB.Create(ClassA2, TClassA2);
  ClassB.Display(Memo1.Lines);
  ClassB.Free;

  //Les 2 membres seront du type TClassA
  ClassB := TClassB.Create(ClassA, TClassA);
  ClassB.Display(Memo1.Lines);
  ClassB.Free;

  ClassA.Free;
  ClassA2.Free;
 end ;

A dire vrai, le deuxième argument dans l'appel du constructeur de la classe B peut être de type TClassA ou d'une des classes descendant de cette dernière.

<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
814
Date d'inscription
vendredi 3 novembre 2000
Statut
Membre
Dernière intervention
30 juillet 2009
3
Je dirais : je crois (je ne l'ai jamais fait)



mais il faut changer un peu ta structure.



tu peux envisager d'avoir un champ type "owner" dans ta class B qui
serait un pointeur sur la classe qui le possède (passé dans le create).

ensuite, tu peux aller rechercher la classe réel à l'execution de ce
champ (as & CO). et donc créer un objet de cette meme classe.



mais c'est possible que cetet solution ne marche pas. C'est juste une idée.





Loda
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005

Salut,



Ta solution me convient bien, c'est celle à laquelle j'avais pensé

Mais j'ai le problème de la création d'un objet à partir d'un autre objet, par copie et non par l'instanciation d'une classe.

Comment est-ce possible ?



Thierry
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Soit les classes A, B et A2 (descendant de A) :
unit Unit2;

interface

uses
  Classes;

type
//D&#233;claration forward
  TClassB =   class ;

  TClassA  = class(TObject)
  private
    FClassB: TClassB;
  public
constructor Create;
    destructor Destroy;override;
    procedure Display(Lines: TStrings);virtual;
  end;

  TClassB =   class (TObject)
  private
    FClassA: TClassA;
  public
constructor Create(Aref: TClassA);
    procedure Display(Lines: TStrings); end;

  TClassA2  = class(TClassA)
  public
procedure Display(Lines: TStrings);override;
  end;

implementation

{ TClassA }

constructor TClassA.Create;
begin
inherited;
  FClassB :=  TClassB.Create(Self);
 end ;

destructor TClassA.Destroy;
begin
  FClassB.Free;
  inherited;
end;

procedure TClassA.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A  => ' + Self.ClassName);
  Lines.Append('FClassB = > ' + FClassB.ClassName);
 end ;

{ TClassB }

constructor TClassB.Create(Aref: TClassA);
begin
inherited Create;
  FClassA : = ARef;
end;

procedure TClassB.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('B = > ' + Self.ClassName);
  Lines.Append('FClassA => ' + FClassA.ClassName);
  FClassA.Display(Lines);
 end ;

{ TClassA2 }

procedure TClassA2.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A2  => ' + Self.ClassName);
  Lines.Append('FClassB = > ' + FClassB.ClassName);
 end ;

end.

Et le code li&#233; &#224; un bouton sur une fiche comprenant, par ailleurs, un TMemo :
<CODE>procedure TForm1.Button1Click(Sender: TObject);
var
  ClassA : TClassA;
  ClassB : TClassB;
  ClassA2 :  TClassA2;
begin
  Memo1.Clear;

  ClassA : = TClassA.Create;
  ClassA.Display(Memo1.Lines);
  ClassA.Free;

  ClassA2 :=  TClassA2.Create;
  ClassA2.Display(Memo1.Lines);

  ClassB := TClassB.Create(ClassA2);
  ClassB.Display(Memo1.Lines);
  ClassB.Free;

  ClassA2.Free;
 end ;

Ce court exemple démontre la faisabilité du problème exposé à quelques restrictions près : l'objet de classe TClassB ne pourra pas appeler des méthodes qui ne seraient pas déclarées dans la classe TClassA. L'intérêt serait donc assez limité.</CODE>

<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005

On est sur la bonne voie, mais pas encore tout à fait au but ...



Dans ma classe B, je voudrais instancier un nouvel objet. Par exemple, je modifie la déclaration en



TClassB =   class (TObject)
  private
    FClassA: TClassA;

    FClassAA : TClassA;
  public
    constructor Create(Aref: TClassA);
    procedure Display(Lines: TStrings); 

  end;



et 



constructor TClassB.Create(Aref: TClassA);
begin
inherited Create;
  FClassA : = ARef;

  FClassAA :=  ARef;
 end ;




Si on suit ta proposition, je vais me retrouver avec FClassA =FClassAA,
tous deux de type TClassA ou TClassA2 selon l'appel, mais pointant sur
le même objet. Comment est-il possible de les dissocier ? Il faudrait
faire une copie dynamique de ARef que je puisse implémenter dans le
constructeur de TClassB.



Thierry
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
En réfléchissant un peu, je me suis dit qu'il existait des méthodes clonage dans d'autres langages. En C++, on utilise le terme de "constructeur de copie".
Je me suis inspiré du principe de ce dernier et voici ce qu'il en ressort :

type
//D&#233;claration forward
  TClassB =   class ;

  TClassA  = class(TObject)
  private
    FClassB: TClassB;
  public
constructor Create;overload;
    //Constructeur de copie
*    constructor Create(A: TClassA);overload;
    destructor Destroy;override;
    procedure Display(Lines: TStrings);virtual;
  end;

  TClassB =   class (TObject)
  private
    FClassA: TClassA;
    FClassAA: TClassA;
  public
constructor Create(Aref: TClassA);
    procedure Display(Lines: TStrings);
  end;

  TClassA2  = class(TClassA)
  public
procedure Display(Lines: TStrings);override;
  end;

implementation

{ TClassA }

constructor TClassA.Create;
begin
inherited;
  FClassB :=  TClassB.Create(Self);
 end ;

*constructor TClassA.Create(A: TClassA);
*begin
*  inherited Create;
*  FClassB : = A.FClassB;
*end;

destructor TClassA.Destroy;
begin
  FClassB.Free;
  inherited;
end;

procedure TClassA.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A = > ' + Self.ClassName);
  Lines.Append('FClassB => ' + FClassB.ClassName);
 end ;

{ TClassB }

constructor TClassB.Create(Aref: TClassA);
begin
inherited Create;
  FClassA : = ARef;
  //Utilisation du constructeur de copie
*  FClassAA :=  TClassA.Create(FClassA);
 end ;

procedure TClassB.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('B  => ' + Self.ClassName);
  Lines.Append('FClassA = > ' + FClassA.ClassName);
*  Lines.Append('FClassAA => ' + FClassAA.ClassName);
  FClassA.Display(Lines);
 end ;

{ TClassA2 }

procedure TClassA2.Display(Lines: TStrings);
begin
  Lines.Append('');
  Lines.Append('A2  => ' + Self.ClassName);
  Lines.Append('FClassB = > ' + FClassB.ClassName);
 end ;

end.

NB : J'ai mis un astérisque en face des lignes modifiées ou ajoutées.

<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005

Merci de ton aide.



Je me retrouve effectivement avec 2 objets bien séparés, mais ils sont
de types différents. Or, j'aimerai que les 2 objets soient du même
type, à savoir TClassA2.



Le fait d'utiliser la classe TClassA pour définir mon objet FClassAA
dans le constructeur de TClassB le type d'office. Il faudrait pouvoir
appeler le type de l'objet FClassA, qui, lui, est variable. Je suis à
la recherche de cette fonction magique.



Thierry
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Arghhh !!!!
La mise en page de ce site m'a supprimé l'espace entre Class et Of dans :
//Déclaration d'une métaclasse
TClassAClass = class of TClassA;

Bien évidement, il faut lire
//Déclaration d'une métaclasse
TClassAClass = class of TClassA;


<HR color=#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
lundi 28 avril 2003
Statut
Membre
Dernière intervention
25 octobre 2005

Merci pour la solution, c'est exactement ce que je cherchais.



Cordialement,



Thierry