IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Constructeur et surcharge


Sujet :

Langage Delphi

  1. #1
    Membre éprouvé

    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    janvier 2006
    Messages
    621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : janvier 2006
    Messages : 621
    Points : 1 259
    Points
    1 259
    Par défaut Constructeur et surcharge
    Bonjour à tous,

    voial je vais me prendre une grosse charge mais tant pis, je sèche lamentablement sur un truc simple, masi je dois être fatigué j'y arrive pas.
    J'ai une classe définie comme ca :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Type TPdf = Class
    Constructor create(orientation : string);
    ...
    Je voudrais définir une autre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Type TMonPdf = class(TPdf)
    fnom : string;
    Constructor (Nom : String ; orientation : string);
    et je voudrais à l'appel de constructeur faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    TMonPdf.Constructor (Nom : String ; orientation : string);
    inherited create(orientation);
    fNom := nom;
    Voila. Sauf que je m'y retrouve pas avec les virtual, override, overload, reintroduce et autres... J
    "L'incohérence de ceux qui dirigent et l'incompétence de ceux qui critiquent sont un vibrant hommage à ceux qui exécutent."
    Général George S. PATTON. Messine 1943.

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 087
    Points : 21 255
    Points
    21 255
    Par défaut
    Alors, ton code me semble correcte, il fonctionne je pense !

    Voici un sujet où j'ai mis un peu de overload et virtual sur un constructor,
    l'ayant tapé directement sur le forum et n'ayant pas fait de Delphi depuis 2 ans, il est surement buggé mais je l'espère pas trop !


    Commencons par overload, c'est le plus simple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    type
      TNomPdf = class
      public 
        Constructor Create(orientation : string); overload;
        Constructor Create(Nom : String ; orientation : string); overload;
    Cela permet d'avoir la même fonction Create avec des paramètres différents dans la même classe


    Reintroduce, lui est limite de la bidouille du langage, c'est en gros faire un overload dans une classe hérité sur une méthode marqué virtuelle dans l'ancêtre !
    Reintroduce c'est plus mot un clé pour cacher un Warning qu'autre chose
    W1010 La méthode '%s' cache la méthode virtuelle du type de base '%s' (Delphi)
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre éprouvé

    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    janvier 2006
    Messages
    621
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Chef de projet MOA

    Informations forums :
    Inscription : janvier 2006
    Messages : 621
    Points : 1 259
    Points
    1 259
    Par défaut
    en fait ce que je veux c'est avoir la seconde classe qui dérive de la première en ajoutant un parametre, et pouvoir dans le constructeur appeler l'ancien. Mais comme je viens de me rendre compte que j'ai fait une connerie dans le code, je vais le revoir.
    "L'incohérence de ceux qui dirigent et l'incompétence de ceux qui critiquent sont un vibrant hommage à ceux qui exécutent."
    Général George S. PATTON. Messine 1943.

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 087
    Points : 21 255
    Points
    21 255
    Par défaut
    Dans ton code actuel n'est ce pas déjà le cas ?
    Tu as résumé tellement ton code, fourni le code complet, c'est peut-être un petit détail de syntaxe

    En fait le virtual et override que je n'ai pas eu le temps de t'expliquer impacte surtout les possibilités d'instanciation en passant par les MetaClass par exemple !
    Et encore le constructor est un cas spécial, un peu différent d'une méthode

    Etant sévèrement rouillé en Delphi, je t'avoue qu'il faudrait que je réessaye ce vieux code D7 (j'ai renonmé pour toi ToString en MyToString car depuis ils ont ajoutés ToString dans TObject et donc cela fausse totalement l'appréciation !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    unit uObjetTherorie;
     
    interface
     
    uses
      Classes;
     
    type
      TBase = class(TObject)
         function MyToString: string;
      end;
     
      TDerived = class(TBase)
         function MyToString(const Message: string): string;
      end;
     
      TBaseVirtual = class(TObject)
         function MyToString: string; virtual;
      end;
     
      TDerivedVirtual = class(TBaseVirtual)
         function MyToString(const Message: string): string; reintroduce;
      end;
     
      TSurchargeVirtual = class(TBaseVirtual)
         function MyToString: string; override;
      end;
     
      TRedifVirtual = class(TBaseVirtual)
         function MyToString: string; overload; override;
         function MyToString(const Message: string): string; reintroduce; overload;
      end;
     
      TBaseOverload = class(TObject)
         function MyToString: string; overload; virtual;
      end;
     
      TRedifOverload = class(TBaseOverload)
         function MyToString: string; override;
         function MyToString(const Message: string): string; overload;
      end;
     
      TSurchargeOverload = class(TBaseOverload)
         function MyToString(const Message: string): string; overload;
      end;
     
     
    implementation
     
    { TBase }
     
    function TBase.MyToString: string;
    begin
       Result := 'TBase';
    end;
     
    { TDerived }
     
    function TDerived.MyToString(const Message: string): string;
    begin
       Result := 'TDerived : ' + Message;
    end;
     
    { TBaseVirtual }
     
    function TBaseVirtual.MyToString: string;
    begin
       Result := 'TBaseVirtual';
    end;
     
    { TDerivedVirtual }
     
    function TDerivedVirtual.MyToString(const Message: string): string;
    begin
       Result := 'TDerivedVirtual : ' + Message;
    end;
     
    { TSurchargeVirtual }
     
    function TSurchargeVirtual.MyToString: string;
    begin
       Result := 'TSurchargeVirtual';
    end;
     
    { TRedifVirtual }
     
    function TRedifVirtual.MyToString: string;
    begin
       Result := 'TRedifVirtual';
    end;
     
    function TRedifVirtual.MyToString(const Message: string): string;
    begin
       Result := 'TRedifVirtual : ' + Message;
    end;
     
    { TBaseOverload }
     
    function TBaseOverload.MyToString: string;
    begin
       Result := 'TBaseOverload';
    end;
     
    { TRedifOverload }
     
    function TRedifOverload.MyToString: string;
    begin
       Result := 'TRedifOverload';
    end;
     
    function TRedifOverload.MyToString(const Message: string): string;
    begin
       Result := 'TRedifOverload : ' + Message;
    end;
     
    { TSurchargeOverload }
     
    function TSurchargeOverload.MyToString(const Message: string): string;
    begin
       Result := 'TSurchargeOverload : ' + Message;
    end;
     
    end.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    procedure TFrmTestMemory.BtnTestHeritageClick(Sender: TObject);
    var
       Base: TBase;
       Derived: TDerived;
       BaseVirtual: TBaseVirtual;
       DerivedVirtual: TDerivedVirtual;
       SurchargeVirtual: TSurchargeVirtual;
       RedifVirtual: TRedifVirtual;
       BaseOverload: TBaseOverload;
       SurchargeOverload: TSurchargeOverload;
    begin
       Base := TBase.Create();
       ShowMessage('TBase.Create(); Base.MyToString : ' + Base.MyToString);
       Base.Free();
     
       Base := TDerived.Create();
       ShowMessage('TDerived.Create(); Base.MyToString : ' + Base.MyToString);
       Base.Free();
     
       Derived := TDerived.Create();
       ShowMessage('TDerived.Create(); Derived.MyToString : ' + Derived.MyToString('Salut'));
       Derived.Free();
     
       BaseVirtual := TBaseVirtual.Create();
       ShowMessage('TBaseV.Create(); BaseV.MyToString : ' + BaseVirtual.MyToString);
       BaseVirtual.Free();
     
       BaseVirtual := TDerivedVirtual.Create();
       ShowMessage('TDerivedV.Create(); BaseV.MyToString : ' + BaseVirtual.MyToString);
       BaseVirtual.Free();
     
       DerivedVirtual := TDerivedVirtual.Create();
       ShowMessage('TDerivedV.Create(); DerivedV.MyToString : ' + DerivedVirtual.MyToString('Salut'));
       DerivedVirtual.Free();
     
       BaseVirtual := TSurchargeVirtual.Create();
       ShowMessage('TSurchargeV.Create(); BaseV.MyToString : ' + BaseVirtual.MyToString);
       BaseVirtual.Free();
     
       SurchargeVirtual := TSurchargeVirtual.Create();
       ShowMessage('TSurchargeV.Create(); SurchargeV.MyToString : ' + SurchargeVirtual.MyToString);
       SurchargeVirtual.Free();
     
       BaseVirtual := TRedifVirtual.Create();
       ShowMessage('TRedifV.Create(); BaseV.MyToString : ' + BaseVirtual.MyToString);
       BaseVirtual.Free();
     
       RedifVirtual := TRedifVirtual.Create();
       ShowMessage('TRedifV.Create(); RedifV.MyToString : ' + RedifVirtual.MyToString);
       ShowMessage('TRedifV.Create(); RedifV.MyToString : ' + RedifVirtual.MyToString('Salut'));
       RedifVirtual.Free();
     
       BaseOverload := TBaseOverload.Create();
       ShowMessage('TBaseO.Create(); BaseO.MyToString : ' + BaseOverload.MyToString);
       BaseOverload.Free();
     
       BaseOverload := TSurchargeOverload.Create();
       ShowMessage('TSurchargeO.Create(); BaseO.MyToString : ' + BaseOverload.MyToString);
       BaseOverload.Free();
     
       SurchargeOverload := TSurchargeOverload.Create();
       ShowMessage('TSurchargeO.Create(); SurchargeO.MyToString : ' + SurchargeOverload.MyToString);
       ShowMessage('TSurchargeO.Create(); SurchargeO.MyToString : ' + SurchargeOverload.MyToString('Salut'));
       SurchargeOverload.Free();
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    5 132
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 132
    Points : 11 247
    Points
    11 247
    Par défaut
    Citation Envoyé par arkhamon Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Type TMonPdf = class(TPdf)
    fnom : string;
    Constructor Create(Nom : String ; orientation : string);
    Sur le principe, le code est correct. inherited suffit à appeler la méthode de même nom de la classe ancêtre.

    La question virtual/override se pose dans le cas du polymorphisme : déclarer une variable TPdf mais l'instancier à l'aide d'un type TMonPdf. Sans virtual/override, le compilateur ne pourra pas savoir qu'il y a une redéfinition dans le type réel et n'appellera que le constructeur du type défini. En bref, c'est un contrôle descendant de la classe définie à la classe réelle

    overload permet d'avoir deux (ou plus) méthodes de même nom. Cela permet de choisir un appel en fonction du besoin et de paramètres différents. Alors que virtual/override sérialise la déclaration (seul la dernière est visible) overload les parallélisent (toutes sont visibles). A noter qu'une déclaration contenant les deux spécificateurs virtual overload est possible.

    reintroduce n'est là que pour empêcher l'apparition d'un avertissement à la compilation et n'a aucune utilité en runtime. L'avertissement apparaît lorsqu'une classe ancêtre défini une méthode virtuelle et que la classe courante la redéfini sans la surcharger (sans override).

    La question est donc de savoir si la variable sera déclarée en TPdf ou TMonPdf

  6. #6
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    mars 2004
    Messages
    897
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : mars 2004
    Messages : 897
    Points : 1 533
    Points
    1 533
    Par défaut
    Andnotor and ShaiLeTroll ont bien résumé la surcharge du create.

    Je tenais simplement à ajouter qu'il était également possible de passer par une fonction de classe pour instancier ta classe TMonPdf de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    TNomPdf = class
      public 
        class function NewByOrientation(orientation : string):TNomPdf; 
        class function NewByName(Nom : String ; orientation : string):TNomPdf;
        ....
    Cela reste cependant anecdotique

    En revanche, quelques subtilités avec un constructeur surchargé avec des références de classe dans ce fil de discussion.
    Pensez à utiliser les tags dans le titre.
    Avant de poser une question reportez-vous à la FAQ Delphi
    Respectez les règles du forum.

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 087
    Points : 21 255
    Points
    21 255
    Par défaut
    En C++Builder, c'est très proche de l'idiome "Constructeur Nommé" qui permet d'avoir plusieurs constructeurs avec le même prototype mais avec des noms différents et implementation différentes évidemement

    le Dummy du CreateNew vient de limitation du C++ à supporter les subtilités du Delphi

    On peut aussi évoquer la pattern factory,
    le CreateForm du TApplication est ce qu'il y a de plus proche en Delphi que l'on connait tous
    Dans les profondeurs de la RTL, on trouve bcp de subtilité sur l'utilisation des constructeurs virtuels

    Si tu es curieux des TMetaClass - références de classe :
    Pattern Strategy et Constructeurs Virtuels VCL
    Méthodes Virtuelles appelés depuis un Constructeur

    J'ai essayé de transposer des techniques Delphi en C++Builder, cela peut te donner un idée de la puissance des "constructeurs virtuels" qui sont un cas TRES TRES particulier (proche des méthodes virtuelles de classes)
    Qui suivent leur propre règle subtilement différentes des "méthodes virtuelles" d'instance

    Après, j'ai perdu un peu mes règles Dephiesque, j'aurais surement tendance à mettre trop souvent virtual, test le code là en laissant ou retirant le virtual sur le constructor
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    5 132
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 132
    Points : 11 247
    Points
    11 247
    Par défaut
    Citation Envoyé par Pascal Jankowski Voir le message
    Je tenais simplement à ajouter qu'il était également possible de passer par une fonction de classe pour instancier ta classe TMonPdf de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    TNomPdf = class
      public 
        class function NewByOrientation(orientation : string):TNomPdf; 
        class function NewByName(Nom : String ; orientation : string):TNomPdf;
        ....
    Pour rejoindre Shai, je dirais même qu'il faut les déclarer en constructor

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    TNomPdf = class
      public 
        constructor NewByOrientation(orientation : string); 
        constructor NewByName(Nom : String ; orientation : string);
    Je parlais de polymorphisme précédemment et là la fonction de classe le rend impossible puisque le résultat est forcé dans un type précis. Un constructeur par contre renverra toujours une instance de la classe en cours.
    Le constructeur n'est rien d'autre qu'une fonction de classe, l'objet n'existe pas encore. Les différences sont qu'on ne spécifie pas le type de résultat et que self ne pointe pas sur la classe.

  9. #9
    Membre expérimenté

    Homme Profil pro
    Inscrit en
    mars 2004
    Messages
    897
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : mars 2004
    Messages : 897
    Points : 1 533
    Points
    1 533
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Les différences sont qu'on ne spécifie pas le type de résultat et que self ne pointe pas sur la classe.
    Certes, on ne spécifie pas le type avec le constructeur mais par défaut ce sera celui de la classe (TNomPdf ici) ce qui coïncide avec celui de la fonction de classe décrit dans mon exemple. En l’occurrence, je ne vois aucune différence notable entre les deux approches. Je rappelle que j’ai noté le caractère anecdotique de cette approche.


    Citation Envoyé par Andnotor Voir le message
    Je parlais de polymorphisme précédemment et là la fonction de classe le rend impossible puisque le résultat est forcé dans un type précis.
    Supposons que ce type soit celui d’une classe abstraite et que tous les instances de classe de la liste polymorphe soient toutes descendante de cette classe. Il sera simple de vérifier si elle hérite de notre classe abstraite grâce à la fonction «InheritsFrom» et donc utiliser les méthodes et propriétés communes. En étudiant son type, puis en castant la classe, on accédera à ses propriétés et méthodes spécifiques. Cela ne pose aucun problème dans ce cas précis.

    Supposons maintenant que notre liste polymorphe fasse appel à plusieurs classes abstraites (toutes différentes deux à deux). Passer par la classe de base « TObject » résout le problème et pourtant « le résultat est forcé dans un type précis » pour reprendre ton expression.

    Dans le cas d’une grande complexité du modèle de classes utilisé avec des comportements (Méthodes et propriétés) que l’on peut retrouver sur une ou plusieurs classes avec éventuellement la possibilité d’héritage multiple de certains comportements alors l’utilisation des interfaces objet se révélera pratique.
    On pourra encore gérer simplement le polymorphisme avec une fonction qui analyse précisément les comportements d’une l’instance en utilisant cette fois la fonction «Supports ».
    Le problème reste encore simple à résoudre et on peut encore passer par les fonctions de classe, bien entendu cela serait quelque peu absurde mais pas impossible.

    Pour résumer, on ne se pose pas ces questions au moment où l’on passe à la phase de codage. Le modèle est souvent réfléchi en amont et les bonnes pistes sont en général prises à ce moment là, ce qui fait que cela reste dans tous les cas un faux problème.

    Un exemple simple de gestion d’une liste polymorphique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
     
    unit Unit2;
     
    interface
    uses
      classes, sysUtils, Dialogs;
     
    type
      TmyClass = class
      private
        Fx : double;
        function GetX: double;
        procedure SetX(const Value: double);
      public
        property x : double read GetX write SetX;
        class function New(Param : string):TmyClass;
      end;
     
      TmyClass1 = class
      private
        Fc : char;
        function GetC: char;
        procedure SetC(const Value: char);
      public
        property c : char read GetC write SetC;
        class function New(Param : string):TObject; virtual;
        {Si aucune classe n'hérite de TMyClass1 on peut définir New ainsi :
         class function New(Param : string):TmyClass1; }
      end;
     
      TmyClass1Deriv = class(TmyClass1)
      public
        class function New(Param : string):TObject; override;
      end;
     
      procedure test;
     
     
    implementation
     
    { TmyClass }
     
    function TmyClass.GetX: double;
    begin
      Result := Fx;
    end;
     
    class function TmyClass.New(Param: string): TmyClass;
    begin
      Result := Create;
      with Result do begin
        Fx := Length(Param);
      end;
    end;
     
    procedure TmyClass.SetX(const Value: double);
    begin
      Fx := Value;
    end;
     
     
    { TmyClass1 }
     
    function TmyClass1.GetC: char;
    begin
      Result := Fc;
    end;
     
    class function TmyClass1.New(Param: string): TObject;
    begin
      Result := Create;
      with TmyClass1(Result) do begin
        if Length(Param) > 0 then
          Fc := Param[1]
        else
          Fc := #0;
      end;
    end;
     
    procedure TmyClass1.SetC(const Value: char);
    begin
      Fc := Value;
    end;
     
     
    { TmyClass1Deriv }
     
    class function TmyClass1Deriv.New(Param: string): TObject;
    begin
      Result := Create;
      with TmyClass1Deriv(Result) do begin
        if Length(Param) > 0 then
          Fc := Param[Length(Param)]
        else
          Fc := '.';
      end;
    end;
     
     
    function StrValueFmt(AObject: TObject):string;
    begin
      with AObject.ClassType do begin
        if InheritsFrom(TmyClass)
          then Result := Format('%s : %g',[ClassName,TmyClass(AObject).x])
          else
        if
        {Traiter le cas de la classe dérivée avant le cas de la classe "ancêtre"}
        InheritsFrom(TmyClass1Deriv)
          then Result := Format('%s dérivée de %s : %s',
                         [ClassName,ClassParent.ClassName,TmyClass1(AObject).c])
        else
        if
        InheritsFrom(TmyClass1)
          then Result := Format('%s : %s',[ClassName,TmyClass1(AObject).c])
          else Result := Format('Other type %s',[ClassName]);
      end;
    end;
     
    var
      List: TStringList;
     
    procedure test;
     
      procedure AddItem(AObject: TObject);
      begin
        List.AddObject(StrValueFmt(AObject),AObject);
      end;
     
    begin
      List:= TStringList.Create;
      with List do
        try
          AddItem(TmyClass.New('abcdef'));
          AddItem(TmyClass1.New('abcdef'));
          AddItem(TmyClass1Deriv.New('abcdef'));
          {Aperçu du contenu de la liste}
          ShowMessage(Text);
        finally
          {Liberér les items
          ........}
          {Libérer la liste}
          Free;
        end;
    end;
     
     
     
    end.
    Pensez à utiliser les tags dans le titre.
    Avant de poser une question reportez-vous à la FAQ Delphi
    Respectez les règles du forum.

  10. #10
    Rédacteur/Modérateur
    Avatar de Andnotor
    Profil pro
    Inscrit en
    septembre 2008
    Messages
    5 132
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : septembre 2008
    Messages : 5 132
    Points : 11 247
    Points
    11 247
    Par défaut
    Citation Envoyé par Pascal Jankowski Voir le message
    Supposons que ce type soit celui d’une classe abstraite et que tous les instances de classe de la liste polymorphe soient toutes descendante de cette classe. Il sera simple de vérifier si elle hérite de notre classe abstraite grâce à la fonction «InheritsFrom»
    Si elles dérivent toutes de la même classe, ce test est inutile.

    Citation Envoyé par Pascal Jankowski Voir le message
    En étudiant son type, puis en castant la classe, on accédera à ses propriétés et méthodes spécifiques.
    Pas à l'aide d'un transtypage "fort" : (Obj as TMonObj). Le transtypage "faible" TMonObj(Obj) lui s'en fiche puisque Obj est traité comme un pointeur non typé. Dans tous les cas, un codage conséquent et superflu qui ne conviendra qu'à une classe très simple.

    Citation Envoyé par Pascal Jankowski Voir le message
    Passer par la classe de base « TObject » résout le problème et pourtant « le résultat est forcé dans un type précis » pour reprendre ton expression.
    Tu peux tout transtyper et par conséquent retourner un TForm si ça te chante, mais ça ne résout rien si ce n'est court-circuiter le compilateur et des VA en runtime difficiles à debugger.
    Pourquoi d'ailleurs typer le résultat en TObject ? Autant retourner simplement un pointer

    Citation Envoyé par Pascal Jankowski Voir le message
    ...alors l’utilisation des interfaces objet se révélera pratique.
    Ce qui est un tout autre sujet.

    Citation Envoyé par Pascal Jankowski Voir le message
    On pourra encore gérer simplement le polymorphisme avec une fonction qui analyse précisément les comportements d’une l’instance...
    Par définition, on ne devrait pas avoir à s'en occuper. Devoir systématiquement tester la classe et faire un branchement n'a plus rien de polymorphe.

    Citation Envoyé par Pascal Jankowski Voir le message
    Pour résumer, on ne se pose pas ces questions au moment où l’on passe à la phase de codage. Le modèle est souvent réfléchi en amont et les bonnes pistes sont en général prises à ce moment là, ce qui fait que cela reste dans tous les cas un faux problème.
    Pas d'accord ! Le comportement est réfléchi et débattu mais la façon dont sont ensuite codées les classes est immuable :
    • Héritage : les inherited où ils doivent l'être pour assurer l'initialisation des données ou la base du comportement ;
    • Encapsulation : Accéder aux variables par des Getter/Setter plutôt que les déclarer simplement "public" ;
    • Polymorphisme : faire abstraction de la classe réelle.

    Tu peux bien sûr faire autrement (sans appliquer la POO), mais l'évolution sera réduite à néant et tu pourras tout repenser à la première mise à jour

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 087
    Points : 21 255
    Points
    21 255
    Par défaut
    Citation Envoyé par Pascal Jankowski Voir le message
    Dans le cas d’une grande complexité du modèle de classes utilisé avec des comportements (Méthodes et propriétés) que l’on peut retrouver sur une ou plusieurs classes avec éventuellement la possibilité d’héritage multiple de certains comportements alors l’utilisation des interfaces objet se révélera pratique.
    Déjà qu'en POO, l'héritage d'implémentation est considéré comme une mauvaise chose,
    pourtant c'est une pratique Delphi très répandu,
    alors l'héritage d'implémentation mutliple, c'est le comble !


    Mieux vaut un objet supportant une série d'interface
    et utilisant de la délégation,
    cela permet en plus de fournir des implémentations différentes en utilisant une factory pour créer l'objet gérant le code délégué
    C'est le principe de composition d'objet

    Citation Envoyé par Pascal Jankowski Voir le message
    On pourra encore gérer simplement le polymorphisme avec une fonction qui analyse précisément les comportements d’une l’instance en utilisant cette fois la fonction «Supports ».
    Le Supports est TRES TRES puissant, je l'ai utilisé pour mon système de driver de périphérique en C++Builder
    Driver au sens général, pas driver windows juste driver au sein du logiciel

    Dans mon système de driver de périphérique, tous mes objets sont des "hardware"
    A ce moment, j'ignore ce qu'ils savent faire, ils ont juste un lien minimal pour la DB
    Ensuite, tu as des Sensor, Caméra, Enregistreur, Sirène, ... tu associe un Hardware à un purpose (un purpose est lui même décomposé en une série de Features)
    un Sender à une Feature de détection
    une Caméra à une Feature de capture vidéo et\ou une Feature d'affichage
    un Enregistreur à un Feature pour enregistrer un capture vidéo et\ou une Feature de gestion d'un pool de Caméra
    une Sirène à un Feature de bipper, de clignoter...

    Chaque Feature correspond une interface avec GUID, souvent la Feature est juste une Factory
    Ensuite cela fourni instancie l'objet contenant l'implémentation de la Feature avec différentes Capabilities
    une Capability est aussi une interface avec GUID

    Si l'utilisateur veux créer une caméra,
    En fait, l'utilisateur sans le savoir va :
    - créer un hardware
    - associer hardware avec un purpose Caméra

    Le programme pourra interroger le purpose pour savoir si gère un Feature
    Cela conditionnera l'utilisation de cette caméra au sein de l'appli

    Si ta Caméra intègre une Feature Bipper, comme c'est un hardware,
    il sera disponible dans la liste des Bipper au même titre qu'une sirène,
    le Feature Bipper lui même ayant différentes versions d'implémentations, via support tu peux connaitre ses Capabilities

    [C++\Delphi] Interface, Héritage et Supports !
    Si tu navigues dans les liens, j'ai poussé le Supports à l’extrême pour avoir un code très souple pour gérer des capacités matérielles différentes selon l'appareil, l'API, ...

    Cela me permet d'intégrer un périphérique avec un nombre de fonctionnalité MINIMALE en implémentant 2-3 interfaces sur les 200 qui existent,
    on peut ainsi livrer rapidement un Driver sans devoir tout gérer,
    ainsi le client apprécie d'avoir une 1ère intégration de ses périphériques dans le logiciel de supervision
    Si il en veut plus, on lui facture le développement du driver adapté à son matériel en fonction de ses demandes.
    Si l'on a déjà écrit le driver, ça c'est le boulot du commercial !

    C'est ainsi que l'on peut fédérer plusieurs marques de périphérique similaire au sein d'un même logiciel en utilisant les API propriétaires de chacun

    on peut fournir un contrôle plus ou moins abouti selon la marque,
    toujours une question de budget développement, et faciliter d'avoir les documentations ou autorisations sur l'utilisation des API,
    cela permet une vue d'ensemble via notre logiciel et facilite l'accès aux logiciels propriétaires des périphériques pour les parties non intégrées au driver




    Citation Envoyé par Pascal Jankowski Voir le message
    Je rappelle que j’ai noté le caractère anecdotique de cette approche.
    Parce que tu ne l'applique pas de la bonne façon !
    En Delphi, les constructeurs nommés existent autant en profiter

    Citation Envoyé par Andnotor Voir le message
    Pour rejoindre Shai, je dirais même qu'il faut les déclarer en constructor .
    Pour la plupart des cas, je suis tout à faire d'accord !

    Tu peux faire ce genre de méthode de classe pour réaliser une petite Factory.
    Au lieu d'un instancier le type en retour de la méthode, tu peux instancier un type hérité, par exemple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    TBidule = class
    public 
      class function Factory(AFactor: TObject): TBidule; 
      constructor Create(AFactor: TObject); virtual;
    TBiduleClass = class of TBidule;
     
    TTruc = class(TObject)
    TChose = class(TObject)
     
    TBiduleTruc = class(TBidule)
    private
      FTruc: TTruc;
    ...
     
    TMachinChose = class(TBidule)
    private
      FChose: TChose;
    ...
     
    class function Factory(AFactor: TObject): TBidule; 
    var
      lClass: TBiduleClass;
    begin
      lClass := TBidule;
      if AFactor is TTruc then
        lClass := TBiduleTruc
      else if AFactor is TChose then
        lClass := TMachinChose; 
     
      Result := lClass.Create(AFactor);
    end;
    Cette exemple de Factory pourrait servir à choisir la classe adaptateur ou proxy, au final on pourrait aboutir à une strategy

    TBiduleTruc est un adaptateur entre TBidule et TTruc
    TMachinChose celui entre TBidule et TChose

    En faisant un Map<TClass, TBiduleClass> on pourrait avoir un code de Factory bien plus souple et supportant plus facilement des extensions !

    Si l'on regarde le code VCL, on retrouve TComObjectFactory qui est une version bien plus abouti de ce genre de démarche, utilisant une Registry, le Factor étant la Clé GUID ...
    Le code n'utilise pas de méthode de classe virtuelle mais plutôt un singleton avec des méthodes d'instances virtuelles, c'est un peu plus élégant !



    Citation Envoyé par Pascal Jankowski Voir le message
    Un exemple simple de gestion d’une liste polymorphique
    Vu que tu finis par du code procédurale avec StrValueFmt, tu gaches tout ton polymorphisme !

    Si tes objets implémentaient par exemple ToString, ton code aurait montré vraiment la puissance du polymorphisme, autant faire un exemple qui soit cohérent jusqu'au bout,
    pourquoi le code de démo ne profite-t-il pas de ce que l'on souhaite démontrer ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function TmyClass.ToString(); 
    begin
      Result := Format('%s : %g',[ClassName, x]);
    end;
     
    function TmyClass1Deriv.ToString(); 
    begin
      Result := Format('%s dérivée de %s : %s',
        [ClassName, ClassParent.ClassName, c]);
    end;
     
    function TmyClass1.ToString(); 
    begin
      Format('%s : %s',[ClassName, c]);
    end;
     
     
    function StrValueFmt(AObject: TObject):string;
    begin
      Result := AObject.ToString;
      if Result = '' then
        Result := Format('Other type %s',[ClassName]);
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Discussions similaires

  1. Réponses: 13
    Dernier message: 28/09/2009, 18h22
  2. constructeurs et surcharge
    Par ProgVal dans le forum C++
    Réponses: 5
    Dernier message: 06/04/2008, 13h28
  3. [POO]Probléme de constructeur virtuel surchargé
    Par Laurent Dardenne dans le forum Delphi
    Réponses: 10
    Dernier message: 15/08/2006, 12h19
  4. Constructeur par défaut en cas de surcharge
    Par gigi_m dans le forum MFC
    Réponses: 4
    Dernier message: 08/06/2005, 09h58
  5. [Constructeur]Pb avec la surcharge d un constructeur
    Par cmoulin dans le forum Langage
    Réponses: 3
    Dernier message: 26/04/2004, 09h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo