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

Composants VCL Delphi Discussion :

Création de metaclass en dynamique


Sujet :

Composants VCL Delphi

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 9
    Points : 5
    Points
    5
    Par défaut Création de metaclass en dynamique
    Quelqu'un sait-il comment créer une classe héritée d'une autre classe en dynamique?
    Cloner la VMT avec un nouveau TClass pour la nouvelle classe.
    J'ai trouvé un source sur le net mais il ne me parait pas "sans bug" ni complet.

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 789
    Points : 25 777
    Points
    25 777
    Par défaut
    La VMT étant en réalité une constante ...
    J'ai des gros doutes sur ce fameux code sur le net, tu aurais pu fournir le lien pour que l'on puisse le voir et donner son avis !

    Quelle est la problématique d'origine ?
    Il y a peut-être plus simple ?

    Tu veux en fait créer des classes à la volée, Delphi n'est pas du JavaScript ou du PHP et les accesseurs Magiques, c'est un langage compilé, on produit pas du code comme ça (bon en fait, si certains ont le courage d'écrire dans une zone mémoire des Codop et de les exécuter mais rare ont assez de génie (folie) pour l'utiliser en réalité, surtout dans un projet professionnel qui un jour sera repris par un stagiaire)

    Peut-être qu'un tableau associatif <String, Variant> pour te permettre d'étendre virtuellement les propriétés d'un objet
    Ce n'est pas loin d'un TDataSet finalement dans l'idée avec un nombre de colonne variable ... tu pourras reprendre l'idée d'une collection de Variant en plus léger que le TClientDataSet

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 9
    Points : 5
    Points
    5
    Par défaut
    voili voilou pour le lien

    http://www.koders.com/delphi/fidCAC2...273.aspx?s=pos

    c'est une idée que j'ai eu avec cet article :
    http://hallvards.blogspot.com/2007/0...s-part-ii.html

    par exemple :
    j'ai une table en base de donnees.
    avec le desciptif de cette table cad la liste des champs et ses types
    exemple
    table Produits
    champ NomProduit string (30)
    champ couleurproduit string (5)

    je veux creer
    1)une table de description qui contient la description de la table : le nom et le type de champs (cidessus)

    et
    2)une table qui contient les donnees d'1 enregistrement (une ligne dans la table) cad les valeurs ('PANTOUFLE', 'NOIRE')
    cette table pointe sur la table descriptif (1) pour connaitre le desc de chaque champ sans devoir le dupliquer à chaque fois
    mais comme c'est un objet par ligne dans la table, je ne veux pas dupliquer pour chaque objet le pointeur qui pointe sur la table descriptif
    je veux mettre ce pointeur comme variable statique à la classe
    OR cette variable sera commune a chaque objet de cette classe

    c'est pourquoi je veux pouvoir dupliquer cette classe en dynamique
    comme cela chaque table aura sa PROPRE classe
    mais des descriptif differents

    suis-je clair?
    la classe produit aura sa propre classe produit (meme si elle derive de la meme base), la classe article aura sa propre classe...

    chaque classe aura les mêmes fonctions mais elles auront chacunnes des descripteur different et statique.

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 789
    Points : 25 777
    Points
    25 777
    Par défaut
    euh, class property, ça doit exister dans les Delphi récent !

    sinon, une variable globale dans la section Implementation, et une class function Get... pour D7 et moins

    ou encore un tableau associatif <TPersistentClass, TPropertyDescription>.

    TPersistentClass est une MetaClass
    TPropertyDescription serait l'instance qui décrit les propriétés
    Selon les Delphi, tu dois avoir les Templates (Génériques)
    Sinon, une TStringList permet d'avoir un tableau associatif assez rapidement !

    Enfin le TDataSet et FieldsDefs, cela fonctionne très bien aussi !

    Tu devrais commencer par savoir les bases du langage Delphi avant de vouloir utiliser du code ASM que tu ne comprends pas !




    Tu es en train de concevoir une couche OR Persistance ?
    Comme Bold InstantObjects, ECO, le modèle de phplive, ... il est possible que SJRD ait produit des objets qui pourrait t'aider dans les RTTI (peut-être même pouvant aller à du Scripting)

    D'ailleurs, tu pourrais voir chez TMS si leur outil de PascalScript pourrait te servir !

    Mais tu peux aussi le faire avec des objets sans solution extrème !
    Tu peux gérer une Collection qui associe une TClass à une Description de structure, ton objet pour accéder à la collection pour sa Description

    J'ai conçu une couche de persistance aussi, j'avais une bonne trentaine de classe ! J'ai refait une mini couche OR en C++Builder, j'ai pour le moment, le minimum

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class DELPHICLASS TShaïORPersistentManager;
    class DELPHICLASS TShaïORPersistentClassMetaData;
    class DELPHICLASS TShaïORPersistentClassMetaDataItem;
    class DELPHICLASS TShaïORPersistent;
    typedef TClass TShaïORPersistentClass;
    Si tu es curieux, juste la partie Interface (en Delphi) d'une des 10 unités de la EpcPersistance que j'ai conçu chez un ancien employeur, juste 335 lignes pour la partie interface, je te laisse imaginer le code de la partie implémentation ... ça montait à entre 15K et 20K lignes pour la lib entière

    Evidemment ces objets sont abstraits, ils servent d'objet de base pour l'implémentation d'objet métier contenant des propriétés publiées
    Dans la couche Delphi, c'est un XML qui donne la structure de la Table MySQL, ainsi que la correspond nom de champ et propriété, les relations (1-1, 1-N, N-N).

    Dans le code, j'utilise les objets métiers fortement typés qui héritent du type abstaits (exemple TClient, TFournisseur, TCommande, TCompta...) qui fournissent des fonctions liées au métier et à la cohérence des données

  5. #5
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    Salut Shai,

    non en fait il y a une subtile nuance mieux expliquée dans la partie I du blog. Il est question d'une propriété de classe spécifique à chaque classe, y compris chaque dérivé.

    j'en ai eu besoin dans un cas de figure particulier, j'avais une série d'objets créés et détruits assez fréquemment ce qui est très pénalisant au niveau performances. J'ai donc cherché a conservé les instances libérées pour les réutiliser après coup.

    Au départ j'étais parti sur une chose comme ça

    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
     
    type
      TCollectedObject = class  
      private
       FNext: TCollectedObject;
      public
        class function New(): TCollectedObject;
        procedure Release;
      end;
     
    var
      Pool: TCollectedObject:
     
    class function TCollectedObject.New(): TCollectedObject:
    begin
      if Pool = nil then
      begin
        Result := Pool;
        Pool := Result.FNext;
      end else begin
        Result := TCollectedObject.Create();
      end;
    end;
     
    procedure TCollectedObject.Release;
    begin
      FNext := Pool;
      Pool := Self;
    end;
    mis à part que Pool peut être placé en variable de classe, cette approche présente deux gros inconvénients, le constructeur n'accepte aucun paramètre, et pire encore le Pool mélange les classes dérivées, on ne peut donc en réalité utiliser qu'un seul dérivé.

    Je suis donc parti sur une autre approche qui surchage directement NewInstance et FreeInstance (plus de problème de constructeur) et qui exploite ClassInfo pour identifier la classe en cours ({$M+} obligatoire).
    Au lieu d'avoir un Pool simple comme au dessus j'ai un truc comme ça

    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
     
    type
    {$M+}
      TCollectedObject = class(TObject)
      private
        FNext: TCollectedObject;
      public
        class function NewInstance: TObject; override;
        procedure FreeInstance; override;
      end;
    {$M-}
    type
      PGarbage = ^TGarbage;
      TGarbage = record
        Cls   : TClass;
        List  : TCollectedObject;
        Next  : PGarbage;
      end;
     
    var
      Garbage: PGarbage;
     
    class function TCollectedObject.NewInstance: TObject;
    var
      Hook : PGarbage;
    begin
    // $M+ obligatoire sinn ClassInfo est vide
      Assert(ClassInfo <> nil);
    // Liste des Garbage
      Hook := Garbage;
    // On recherche celui de notre classe
      while (Hook <> nil) and (Hook.Cls <> ClassInfo) do
        Hook := Hook.Next;
    // S'il n'existe pas, laisser Delphi faire son boulot normalement
      if (Hook = nil) or (Hook.List = nil) then
      begin
        Result := inherited NewInstance;
      end else begin
    // Sinon prendre la première instance disponible
        Result := Hook.List;
    // Et la retirer de la liste
        Hook.List := TCollectedObject(Result).FNext;
        // InitInstance(Result); // <- very slow !
      end;
    end;
     
    procedure TCollectedObject.FreeInstance;
    var
      Hook : PGarbage;
    begin
      //CleanupInstance; // you have to clean everything in the destructor
    // Recherche le Garbage propre à la classe
      Hook := Garbage;
      while (Hook <> nil) and (Hook.Cls <> ClassInfo) do
        Hook := Hook.Next;
    // S'il n'existe pas, le créer
      if Hook = nil then
      begin
        New(Hook);
        Hook.Cls := ClassInfo;
        Hook.List := nil;
        Hook.Next := Garbage;
        Garbage := Hook;
      end;
    // Ajouter l'objet à la liste
      FNext := Hook.List;
      Hook.List := Self;
    end;
    Notez au passage que j'ai supprimé les appels à InitInstance et CleanupInstance qui sont aussi très consommateur de temps, la seule conséquence et que l'objet ressortant du Garbage n'est pas initialisé (il conserve les anciennes valeurs), il faut donc le faire explicitement soit dans le constructor ou plus logiquement dans le destructor..

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 9
    Points : 5
    Points
    5
    Par défaut
    "Tu devrais commencer par savoir les bases du langage Delphi avant de vouloir utiliser du code ASM que tu ne comprends pas !"
    t'es gentil!
    20ans de dev, C++,Asm,SQL, 10 ans de delphi, j'ose croire que je ne suis pas si débutant que ça...
    je trouve simplement que le code du rttimaker me semble léger et pas complet c'est tout. je ne jamais dis que je le comprennais pas.

    L'idée me semblait simplement interessante à creuser!

    Paul TOTH> interessant le concept!

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 789
    Points : 25 777
    Points
    25 777
    Par défaut
    zorglub59, cela te coûte à ce point de "dupliquer pour chaque objet le pointeur qui pointe sur la table descriptif"
    Perso, je l'ai fait récemment en C++ dans ma mini-couche
    Code c++ : 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
     
    //---------------------------------------------------------------------------
    //                           TCensureORPersistent                           -
    //---------------------------------------------------------------------------
    /**
     * constructor create an empty objet
     */
    /*constructor*/__fastcall TCensureORPersistent::TCensureORPersistent()
    {
      InternalCreate();
    }
     
    //---------------------------------------------------------------------------
    /**
     * constructor create an objet and fill published properties with dataset
     * @param[in] ADataSet contains fields associate with properties, reference not stored
     */
    /*constructor*/__fastcall TCensureORPersistent::TCensureORPersistent(TDataSet* ADataSet)
    {
      InternalCreate();
      SetPublishedPropertiesFromDataSet(ADataSet);
    }
     
    //---------------------------------------------------------------------------
    void TCensureORPersistent::InternalCreate()
    {
      FMetaData = TCensureORPersistentManager::GetInstance()->InitializeClassMetaData(this->ClassType(), this->IsRelation());
    }

    Effectivement, chaque instance contient un FMetaData qui pointe sur un TSygalORPersistentClassMetaDataItem (ces objets étant stockés dans une TObjectList)
    Après tout, le stockage de la référence est un gain sur la recherche dans le tableau associatif (InitializeClassMetaData en plus fonctionne en Lazy Loading)

    Pour ta gestion des objets par ligne d'une table !
    Tu pourrais le gérer autrement !
    Mes objets soit utilise un DataSet avec deux modes
    soit une copie vers les propriétés publiqués (le dataset peut être ainsi libérer)
    soit on affecte un DataSource à l'objet, et dans ce cas, il fonctionne comme un Curseur !
    Ainsi, j'ai une instance unique de l'objet, je me déplace dans le DataSet (typiquement relié à un DBGrid ou DBEdit), le contenu du l'objet évolue (parce qu'en interne, il utilise le DataSource)
    En général, avec un DBGrid c'est en lecture seule (souvent, il manque même des Fields, je peux avoir que 2-3 Fields renvoyé par le SELECT alors qu'il y 10 published, l'écran n'a aucune raison d'aller voir d'autres champs, donc l'objet ne fait un SELECT que sur les colonnes nécessaires)
    Pour modifier, cela passe par un autre Formulaire, le SELECT généré par l'objet recupère tous les champs ce coup-ci, idem l'objet utilise le DataSource connecté au DBEdit... et les UPDATE\INSERT seront générés par une méthode Save (oui, pas de Post sur le DataSet utilisé juste comme intermédiaire de saisie, vivement les LiveBindings)

    A chaque fois l'objet est utilisé comme encaspulation du DataSource\DataSet
    Par contre, dans d'autres sections du code où l'objet est utilisé pour des calculs, le DataSet est Ouvert, Copié et libéré !

    Il faut éviter les collections de 10000 objets (d'autres sur le forum se sont mordus les doigts avec ces mauvaises pratiques !)

    Sur ma précédent couche, j'ai fait un comportement proche mais encore plus poussé
    Je lançais un Select sur l'objet qui renvoie une collection d'objet !
    A ce moment, la collection ne contient aucun objet en réalité, juste le DataSet, un accesseur fournissait le même principe de curseur, tant que l'on utilisait l'objet en lecture, c'était le même, mais à partir du moment où l'objet était modifié ou que sa référence pouvait être utilisé par un autre objet, automatiquement cela créait un clone qui copiait les données et était conservé dans un cache
    Dans la plupart des traitements, on avait donc que le curseur !
    Le besoin d'instance spécifique étant finalement assez rare !



    Exemple d'accesseur standardisé :
    Code c++ : 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
     
    //---------------------------------------------------------------------------
    int __fastcall TSygalORPersistent::GetIntegerProp(int &AIntegerMember, const AnsiString &PropName/* = ""*/)
    {
      if (DataSource)
        AIntegerMember = FieldByPropertyName(PropName)->AsInteger;
     
      return AIntegerMember;
    }
     
    //---------------------------------------------------------------------------
    void __fastcall TSygalORPersistent::SetIntegerProp(int &AIntegerMember, const int Value, const AnsiString &PropName/* = ""*/)
    {
      AIntegerMember = Value;
     
      if (DataSource)
        FieldByPropertyName(PropName)->AsInteger = AIntegerMember;
    }


    Désolé, si je t'ai vexé !
    Mais ta question à la lecture des articles cités, me semblait déjà être résolu par des méthodes basiques !

    Citation Envoyé par Paul TOTH Voir le message
    Il est question d'une propriété de classe spécifique à chaque classe, y compris chaque dérivé.
    Mais j'avais bien compris, d'où l'utilisation d'un tableau associatif !
    En fait je pensais à class property utilisant un Accesseur Abstrait redéfini pour chaque classe héritée !

    Je ne comprends pas bien le problème et le besoin d'un code aussi retord pour quelques choses d'aussi simple !

    Personnellement, n'ayant pas dépassé Delphi 7 (en ce moment, je suis en C++Builder 2007), il n'y aucune supprise à la lecture du Blog

    Ce qui je trouve dingue c'est d'avoir autant de code (de malade de la partie 2) pour une chose aussi simple !

    Reprenons l'exemple du blog

    Version D7 / D2007 !
    Pour D2007 ,c'est un code supposé, est-ce possible, je l'espère, cela reprend la section "Explicit per-class class variables implementation")
    Dans le blog, je ne comprends pas pourquoi, il n'y a pas eu l'utilisation d'un Accesseur Get au lieu d'utiliser directement la variable !
    Il manque pour moi, une variante importante avant d'aller dans un truc aussi dingue
    Si c'est un projet professionnel qui maintient le code en cas d'erreur ou d'évolution du compilateur qui change la gestion du code ASM comme le 64bits ou tout simplement une modification de l'adresse de la VMT

    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
     
    type
      PDescriptor = ^TDescriptor;
      TDescriptor = class...
     
      TFruit = {abstract} class
      proctected
        class function DescriptorStorage: PDescriptor; virtual; abstract;
      public
        constructor Create;
        {$IFDEF D2007UP}
        class function GetDescriptor: TDescriptor;
        {$ELSE}
        class property Descriptor: TDescriptor read GetDescriptor;
        {$ENDIF}    
      end;  
     
      TApple = class(TFruit)
        {$IFDEF D2007UP}
        class var FDescriptor: TDescriptor;
        {$ENDIF}
        class function DescriptorStorage: PDescriptor; override;
      end;
     
      TOrange = class(TFruit)
        {$IFDEF D2007UP}
        class var FDescriptor: TDescriptor;
        {$ENDIF}
        class function DescriptorStorage: PDescriptor; override;
      end;
     
    implementation
     
    var
    {$IFNDEF D2007UP}
      _TApple_Descriptor: TDescriptor = nil;
      _TOrange_Descriptor: TDescriptor = nil;
    {$ENDIF}
     
    class function TFruit.GetDescriptor: TDescriptor;
    var
      Ptr: PDescriptor;
    begin
      Ptr := Self.DescriptorStorage;
     
      if not Assigned(Ptr^) then
        Ptr^ := TDescriptor.DescriptorRegistry.Items[Self.ClassType]; // ça je te laisse gérer ton Instance Registry et Factory de ton objet de Description
     
      Result := Ptr^;
    end;
     
    class function TApple.DescriptorStorage: PDescriptor;
    begin
    {$IFDEF D2007UP}
      Result := FDescriptor;
    {$ELSE}
      Result := @_TApple_Descriptor;
    {$ENDIF}
    end;
     
    class function TOrange.DescriptorStorage: PDescriptor;
    begin
    {$IFDEF D2007UP}
      Result := FDescriptor;
    {$ELSE}
      Result := @_TOrange_Descriptor;
    {$ENDIF}
    end;
    Le code reste simple, certe un petit peu de "copier-coller" à chaque classe héritée, suffit de se faire un fichier exemple d'une classe minimale (le abstract forcera l'implémentation au pire)
    Mieux vaut ça que trop de délire !
    Personnellement, je suis allé parfois trop loin dans les délires, conséquence, aucun de mes collègues ne comprenaient le code (mon remplaçant s'arrache les cheveux !)

    Je suppose que chaque classe métier aura sa propre unité (ce que j'ai fait pour les objets de chaque application utilisant la couche de persistance), le code pouvant être long, cela évite de tout mélanger (plusieurs classes pouvant être ensemble si fortement lié comme Commande et LigneDeCommande)

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 9
    Points : 5
    Points
    5
    Par défaut
    "dupliquer pour chaque objet le pointeur qui pointe sur la table descriptif"
    > c'est pas genant mais c'est dans chaque objet et un objet est une ligne ds la table
    donc a chaque nouvelle ligne il faut le faire .
    (juste pour optimisation)

    le pb ce n'était pas l'implantation dans la classe mais la duplication des classes à la volée (la VMT)
    y'a tjs d'autres solutions plus simple (a l'epoque je pense que D2007 n'existait pas)
    mais c'était juste une recherche perso.

    Ton systeme a une faille : il faut connaitre toute les tables d'avance puisque tu crées une classe par table! (si j'ai bien tt compris)

    le but c'est de créer autant de classe qu'il y a de table dans la base!
    avec un descriptif pour chaque classe
    et donc pouvoir allouer un objet "correct" pour chaque classe

    c'est un beau challenge !!

  9. #9
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Le code reste simple, certe un petit peu de "copier-coller" à chaque classe héritée
    Mais c'est juste le point clé du problème Je ne voulais pas ajouter une gestion spécifique à chaque dérivé mais simplement dériver d'une classe qui s'occupe de tout (le tout sous Delphi 6 en l'occurence)

    Citation Envoyé par zorglub59 Voir le message
    le but c'est de créer autant de classe qu'il y a de table dans la base!
    avec un descriptif pour chaque classe
    et donc pouvoir allouer un objet "correct" pour chaque classe

    c'est un beau challenge !!
    en fait je n'ai pas compris ce que tu veux faire, j'ai juste repris un exemple du blog sur un cas que j'ai recontré

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 789
    Points : 25 777
    Points
    25 777
    Par défaut
    Le Problème, c'est qu'il semble que vous instanciez tous les deux TROP d'objet !
    C'est couteux, totalement inutile, j'espère au moins que vous utilisiez des mécanismes de Lazy Loading ou Lazy Initialization !

    Pour le recyclage d'instance, oui, une fois javais bossé sur une Factory avec une Registry d'objet inactivé, les gains sur les performances n'était pas si intéressant que cela par rapport à un système d'objet Curseur (on peut pas lutter, une instance unique sera toujours plus rapide qu'en allouant des milliers même avec un recyclage !)
    Je précise que j'utilise systématiquement une base de données, donc forcement du TDataSet !
    Il est vrai qu'avec un modèle XML par exemple, on peut-être tenter de créer des milliers d'objet !
    On voit ce que cela donne avec le TXMLDocument ou TTreeView !
    Disons tant que cela tourne dans les 10K instances, ça reste supporte, lorsque l'on manipule des tables avec 10M d'enregistrement, faut revoir sa copie mais ça c'est si l'on veut conserver un modèle objet *, car faut pas rêver, un code objet même si plus élégant est souvent plus lent qu'un code bien brutal sans architecture réel, surtout si l'on déplace les calculs dans des SQL\PL-SQL

    Je pense à l'utilisation des DataSet avec PackedRecord (avec Async Fetch) pour justement chargé les données juste au moment utile, le fait de créer des collections souvent se fait dès la création, du coup, on peut chercher toutes les optimisations dans son code, on se prive des optimisations de la VCL\Provider\Driver\SGBD
    Encore une fois, bien penser son Lazy Loading pour conserver ce type d'avantage !


    * [HS], lorsque je parlais d'objet, on me répondait TForm ou TDataModule, quand je parlais Design Pattern, on me répond OnClick (ben oui le DesignTime sous l'IDE)
    Le seul développement objet en Delphi que j'ai vu et maintenu, avait été codé par un développeur Java, les 95% des développeurs Delphi que j'ai cotoyé professionnellement utilisait Delphi pour ce qu'il est pour bcp, un RAD et un IDE, on pose ses composants, on mets tout le code dans le Form (y compris le SQL), on duplique l'intelligence métier dans tous les TForm qui manupileront les tables, le seul code réutilisable ben c'est la VCL...


    le Modèle, une enregistrement, une instance, c'est bien pour des petites collections d'une centaine d'objets, lorsque l'on gère des millions d'enregistrements, il faut abandonner ces méthodes et penser à un modèle plus souple !


    Citation Envoyé par zorglub59 Voir le message
    "dupliquer pour chaque objet le pointeur qui pointe sur la table descriptif"
    > c'est pas genant mais c'est dans chaque objet et un objet est une ligne ds la table
    donc a chaque nouvelle ligne il faut le faire .
    (juste pour optimisation)
    Tu vas optimiser pas grand chose, ce n'est pas une référence renseigné dans le constructeur en plus ou en moins qui va couter le plus !
    le SQL, la création d'un DataSet et la récupération des enregistrements, c'est déjà long, la création d'objet aussi, mesure l'ensemble du cycle, ce n'est pas 4octets (ou 8) de plus ou de moins par objet qui changeront grand chose !
    C'est de l'optimisation de pacotille !
    Pour le problème, c'est le nombre d'instance, quel est intérêt d'allouer autant d'objet, est-ce qu'au moins tous ces objets seront utilisés par le traitement ?
    As-tu pensé à mon système de Curseur ?

    Citation Envoyé par zorglub59 Voir le message
    Ton systeme a une faille : il faut connaitre toute les tables d'avance puisque tu crées une classe par table! (si j'ai bien tt compris)
    Ah oui, c'est modèle objet orienté métier !
    Un objet pouvant en réalité s'occuper de plusieurs tables (même sous objet invisible à l'intérieur
    Tu veux un modèle nettement plus générique !
    Un modèle fortement "typé" et "modélisé"

    Citation Envoyé par zorglub59 Voir le message
    le but c'est de créer autant de classe qu'il y a de table dans la base!
    avec un descriptif pour chaque classe
    et donc pouvoir allouer un objet "correct" pour chaque classe

    c'est un beau challenge !!
    Je comprends, oui, d'accord, bon c'est un modèle très discutable à mon avis, j'ai participé à un Framework PHP de générateur d'application web (et donc de table dont la structure était défini par d'autres tables et des XML), en fait, je me suis inspiré du TClientDataSet et j'ai bcp utilisé de Tableau associatif du PHP
    La généricité à un coût !
    Comme justement tu perds les optimisations liés à la spécialisation, tu essaye d'en trouver d'autres !

    Mais au final, toi tu n'auras que des TFruit et JAMAIS de vrai TApple ou TOrange codé dans le code, tu veux "générer des classes à la volée"
    Un TFruit aura une MetaClass différent selon la table attachée, et la "MetaClass Dynamique" permettra de savoir lire l'objet !

    En fait, tu veux faire de l'Heritage au RunTime et non au DesignTime !
    On est à la limite de scripting, tu as pensé à utiliser PHP, Python ... ces langages gèrent nativement de la création d'objet à la volée, sans avoir précisément défini la structure même des classes !
    Côté performance, forcément ...

    Tes objets n'auront pas de vrai membre, tu auras une collection de colonne comme les TFields dans chaque objet, et tu auras l'équivalent d'un TFieldDefs dans un objet partagé !
    Tu réinvente une variante du TDataSet en version monoligne avec Meta séparé ...


    C'est un truc défi intéressant, difficilement maintenable, presque inutile dans un projet industriel mais intéressant !

  11. #11
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 466
    Points
    28 466
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Le Problème, c'est qu'il semble que vous instanciez tous les deux TROP d'objet !
    dans mon cas ce n'est pas le nombre, mais la fréquence qui pose problème.

    c'est dans le cadre d'un éditeur Pascal avec coloration sémantique, chaque modification du texte peut amener à recréer tous les objets créés par le parser idéalement je ne devrais recréer que la portion de code impactée, mais dans un premier temps j'ai cherché à optimiser le traitement global qu'il est plus facile de relancer que de déterminer où commence et se termine une portion code.

    exemple classique, je place un { en début de source...tout le reste du code devient du commentaire, j'ajoute un } juste après, tout le reste du code redevient du code...je ne peux pas me permettre d'attendre 3 secondes après l'analyse qui est non pas syntaxique mais sémantique, chaque élément est identifié dans son contexte et pas simplement mis en forme d'après un dictionnaire de mot clés j'ai donc un objet derrière chaque item du source qui gère le Hint, le click etc...et en analysant ce qui prenait du temps, plus de 50% du temps était passé sur GetMem et FreeMem lors de la création/destruction d'objets....

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

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 789
    Points : 25 777
    Points
    25 777
    Par défaut
    D'où le recyclage d'instance, je le comprends bien !
    Effectivement, on avait discuté de cela dans le sujet Création d'objets "par lots"
    @zorglub59, d'ailleurs le sujet cité ci-dessus, devrait te plaire ! c'est proche de ce que tu veux faire avec les collections d'objets


    J'avais prévu dans le code C++ que je maintiens un mécanisme similaire de recyclage d'objet pour éviter d'en recréer (sachant que derrière, il y a d'autres objets et des interfaces venant de DLL ...), pour le moment, ce n'est pas encore codé (j'ai fait l'archi objet et les méthodes vides avec des TODO dedans)
    Pour le moment, je ne sais pas si je vais le mettre en place ou non !
    La fréquence ne sera pas énorme et le nombre non plus (chaque objet représentant un périphérique matériel et leur fonctionnalité principal, exemple une Camera et une Séquence Video)


    Faut dire que gérer une "coloration sémantique" tout en objet, cela ne doit pas être le plus évident !

  13. #13
    Futur Membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 9
    Points : 5
    Points
    5
    Par défaut
    le même probleme se pose pour les docs XML
    souvent les classes qui lisent ces fichiers sont des usines qui bouffent beaucoup de même (1Go j'ai deja vu)
    alors qu'en faisant une structure compacte comme ci-dessus + un pool memoire, je suis sur qu'il y a moyen de faire plus compacte!

Discussions similaires

  1. Création d'un tableau dynamique avec XSL-FO
    Par lionelbrizuela dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 31/01/2006, 12h04
  2. Réponses: 10
    Dernier message: 31/12/2005, 21h10
  3. Création d'un ensemble "dynamique"
    Par petitcoucou31 dans le forum Langage
    Réponses: 3
    Dernier message: 29/12/2004, 19h05
  4. création d'une librairie dynamique
    Par bilo2000 dans le forum Autres éditeurs
    Réponses: 3
    Dernier message: 26/08/2004, 16h17
  5. [Plugin] Création d'une vue dynamiquement
    Par The Bonze dans le forum Eclipse Platform
    Réponses: 2
    Dernier message: 15/06/2004, 14h23

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