Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

Sujet : serialisation: heritage & évolutivité [ Divers / Aide et documentation ] (Guillemouze)

mardi 15 avril 2008 à 09:41:28 | serialisation: heritage & évolutivité

Guillemouze

Salut tout le monde,
je suis sur un probleme de serialisation de mes objets, et je cherche une maniere efficace pour la sauvegarde/chargement.
Ma structure est composée d'une liste d'objets heritants tous de la meme classe de base (a travers de multiples niveaux).
on pourrai les symboliser comme ca :

  A
 /  \
 B   C
/ \ / \
D E F G

Mon but est de pouvoir sauvegarder les informations de ces objets de maniere optimale (en terme de rapidité), tout en gardant à l'esprit que ces structures changeront a l'avenir: ajout/suppression de champs, ajout de classes intermediaires (pas sur mais possible), ...

j'ai donc pensé a la structure suivante:
| Id de la classe | Version de la classe | Champs de la classe | Version de la classe mere | Champs de la classe mere | ...
ce qui me donnera par exemple, pour un objet de la classe E
| E | 2 | E1 | E2 | 5 | B1 | B2 | B3 | 1 | A1

ou les Xi sont les champs de la classe X, en supposant que la classe E est en version 2, la B en 5 et la A en 1.

J'en arrive donc (enfin) a ma question : Que pensez vous de cette structure? est elle fiable, efficace et evolutive? Peut etre avez d'autres idées/suggestions a me soumettre.

Merci de vos réactions :)


mardi 15 avril 2008 à 14:20:27 | Re : serialisation: heritage & évolutivité

Caribensila

Membre Club
Salut Guillemouze,

Si j'ai bien compris...

Je reprends ton exemple pour la classe E et je considère une autre classe D (version 3) par exemple. Ca donnera, pour ces deux classes:

| E | 2 | E1 | E2 | 5 | B1 | B2 | B3 | 1 | A1
| D | 3 | D1 | D2 | 5 | B1 | B2 | B3 | 1 | A1

On voit qu'on sauvegardes deux fois les mêmes données (en rouge)... Ce qui n'est pas optimal.
De plus, s'il y a changement dans la structure, il faudra peut-être revoir toutes les classes contenant les parties qui auront changé.

Mais ai-je bien compris ton problème?...

mardi 15 avril 2008 à 14:58:14 | Re : serialisation: heritage & évolutivité

MAURICIO

Effectivement c' est ce que j' ai remarqué aussi: peut-être veux tu garder pour chaque "child" la version du parent avec lequel il descend lors de la sauvegarde ..

mardi 15 avril 2008 à 18:17:58 | Re : serialisation: heritage & évolutivité

Guillemouze

non il n'y a pas redondance. B1, B2, A1, ... sont les valeurs des champs (pour l'instance de E qui est en train d'etre sauvée) hérités de A et B


Petit exemple un peu plus concret :
Classe Papi - 2 champs: (version 4)
 - Toto: integer
 - Titi : word

Classe Maman - 1 champ: (version 2)
 - Tutu: char

Class Fiston - 1 champ (version 7)
 - Tata: byte

Class Fiston2 - 0 champs (version 5)


soit une instance de fiston (F1) et une instance de fiston2 (F2)
F1 = (Tata=9, Tutu=C, Titi=12, Toto=23);
F2 = (             Tutu=E, Titi=21, Toto=512);
je sauverai donc
pour F1 : ID_FISTON  | 7 | 9 | 2 | C | 4 | 12 | 23
pour F2 : ID_FISTON2 | 5 |     2 | E | 4 | 21 | 512
(en rouge les num de version)

Ainsi, si j'ajoute un champ a Maman par exemple (je passe donc en version 3), je sais que si je li une version 2, je lierai le champ "Tutu", si je lis une version superieur, il faudra aussi que je lise le nouveau champ. En gros, la version m'indique l'espace memoire ocuppée par la classe elle meme

procedure Maman.Read(S: TStream);
var
    v: byte;
begin
    S.Read(v, 1); //lecture de la version
    S.Read(Self.Tutu, 1);
    if v > 2 then //Seulement a partir de la version 3
       S.Read(MonNouveauChamp, 4);

    inherited; // a tour du parent de se lire
end;


Donc comme tu le dis mauricio, je ne peux pas savoir qu'une sauvegarde de la version X d'une classe correspond a une version Y de sa classe mere (mes classes n'evoluent pas toutes en meme temps).

Ai-je été clair?

mardi 15 avril 2008 à 18:22:02 | Re : serialisation: heritage & évolutivité

MAURICIO

Haa bem oui, mais c' est qui le père de "Fiston" et "Fiston2" ?  ^ ^

mardi 15 avril 2008 à 20:34:14 | Re : serialisation: heritage & évolutivité

florenth

Membre Club
Réponse acceptée !
Salut !

Perso, je trouve que ta structure fonctionne bien.
Après, pour l'implémentation dans le code, tu ne donnes pas trop de détails.
Mais j'imagine que la version de chaque classe est bien retournée par une "class function" et que tu utilises bien l'héritage pour sauvegarder et chercher les propriétés ancestrales.

Vu le code que tu donnes, ça a l'air bon, mais j'aurais une toute petite remarque: faire sauvegarder les ancêtres AVANT la classe elle-même. En gros, appeler inherited en tout premier dans les procédures Read ET Write.
Pourquoi ?
Parce que si tu as besoin d'initialisations, ou autre, il est judicieux de s'assurer que les ancêtres ont un contenu cohérent avec ce que tu vas charger.
En effet, si TChild dérive de TParent, une instance de TParent ne peut pas accéder aux membres de TChild alors qu'une instance de TChild peut accéder aux membres de TParent. Et suivant ce que tu fais dans tes procédures, ce détail ne doit pas être négligé (surtout si tes classes évoluent, on ne peut pas prédire ce que va devenir ton code !)

En plus, ça permet facilement de faire sauvegarder ce que tu appelles l'id de classe (qui ne doit être fait qu'une fois par objet sauvé): il suffit de demander à la classe A de le faire ! Par le jeu des inherited, ce sera elle qui sera sauvée en premier alors que dans ton cas, c'est plus dur.

Voila déjà pour ça.

Ensuite, je ne pense pas que tu va pouvoir ajouter des classes intermédiaires dans ta hiérarchie. Dans tous les cas, c'est affreusement galère !

Et j'aurais une autre idée: donner la taille en octets de chaque enregistrement de classe. Car quand tes versions changeront, tu n'auras plus aucun moyen de le savoir. (ex: une classe de version 2 charge un fichier de version 4: il y a des champs en trop mais elle ne peut pas le savoir)

mardi 15 avril 2008 à 20:37:58 | Re : serialisation: heritage & évolutivité

florenth

Membre Club
Tiens, mes dires sont confirmés par Wikipédia:
http://fr.wikipedia.org/wiki/S%C3%A9rialisation#Types_hi.C3.A9rarchiques

mardi 15 avril 2008 à 20:53:15 | Re : serialisation: heritage & évolutivité

florenth

Membre Club
Sinon, si tu cherches en plus la performance (tant au niveau occupation disque que  vitesse de lecture/écriture) j'ai une autre solution, radicale cette fois:

- Comme les versions des classes seront les mêmes pour tout le fichier que tu vas écrire, tu écris une fois toutes ces versions au début du fichier. Comme ça:
  1. C'est fait une fois pour de bon.
  2. Tu gagnes en rapidité et en place.

- Du coup, tu vas devoir faire quelque chose de beaucoup plus complexe sur le plan structural: chaque classe devra être référencée auprès d'une classe de gestion (comme TBitmap et TJpegImage se référencent sur TPicture) qui se chargera de gérer le chargement/sauvegarde de tout ton bazar.

C'est de l'introspection en sorte, mais fait-maison puisque Delphi ne propose rien** pour le faire automatiquement, à la différence de Java et .net.

**: c'est pas très juste (voire complètement faux) car les composants fonctionnent aussi selon ce principe d'introspection (enregistrement dans les dfm) qu'il est tout à fait possible de réutiliser en faisant descendre tes classes de TComponent, en lui faisant surcharger les méthodes adéquates et en utilisant les classes de Delphi faites pour cela (TReader, TWriter, ...)
Il n'y a "juste" pas de compatibilité descendante puisqu'une exception est produite dans ces cas là (mais peut être que tu ne souhaites pas garantir de compatibilité descendante)

Voila, avec ça tu as de quoi pas mal réfléchir !
[d'ailleurs ce problème aurait du être réfléchi AVANT de coder quoi que ce soit !]

mardi 15 avril 2008 à 20:55:01 | Re : serialisation: heritage & évolutivité

florenth

Membre Club
Pardon, je parlais de compatibilité ascendante (cas où on lit un fichier provenant d0une version plus récente) et non pas descendante dans mon dernier paragraphe.

Voir ici: http://fr.wikipedia.org/wiki/Compatibilit%C3%A9_ascendante_et_descendante

mercredi 16 avril 2008 à 00:06:42 | Re : serialisation: heritage & évolutivité

Guillemouze

merci BEAUCOUP florenth pour toutes ces remarques

"Mais j'imagine que la version de chaque classe est bien retournée par une "class function" et que tu utilises bien l'héritage pour sauvegarder et chercher les propriétés ancestrales."
Non j'ai choisi d'utiliser des constantes dans le code pour gerer chaque version. Cette info de version n'est utilisée que par la classe elle meme pour savoir quoi lire. En clair, seules les methodes Read et Write de la classe utilisent cette info, donc je ne suis pas allé jusqu'a creer une class function. Peut etre vais-je faire une fonction Read[Write]Version pour genericiser tout ca... a voir.
par contre il est sur que j'utilise l'heritage, sinon je perd tout l'interet de ce versionning !

"faire sauvegarder les ancêtres AVANT la classe elle-même."
Carrement, tres bonne idée. mes objets n'utilisent aucune donnée de leur parent lorsqu'ils chargent les leurs, mais ca pourrait tres bien arriver. A changer absolument.

"En plus, ça permet facilement de faire sauvegarder ce que tu appelles l'id de classe (qui ne doit être fait qu'une fois par objet sauvé): il suffit de demander à la classe A de le faire ! Par le jeu des inherited, ce sera elle qui sera sauvée en premier alors que dans ton cas, c'est plus dur."
C'est peut etre la que se trouve le gros probleme de ma conception. En effet, l'id est sauvé qu'une fois par objet, pour la classe la plus profonde. Mais cette action est faite par le superviseur (la liste d'objets). Cela vient du fait qu'il faut que j'appele le bon TClass.Create .
Mon code ressemble a peu pres a ca :

procedure TSuperviseur.Load(s: TStream);
begin
    while not EndOfStream(s) do
    begin
       s.read(id, 1);
       classe := Self.GetClassOfId(id);
       MonObjet := classe.Create;
       MonObjet.Load(s);
    end;
end;

procedure TSuperviseur.Save(s: TStream);
begin
    for i := 0 to self.Count-1 do
    begin
       id := Self.GetIdOfClass(self[i].classType);
       s.write(id, 1);
       MonObjet.Save(s);
    end;
end;


"donner la taille en octets de chaque enregistrement de classe"
Ca me semble aussi tres important pour lire des version ulterieures avec un ancien code.

"Comme les versions des classes seront les mêmes pour tout le fichier que tu vas écrire, tu écris une fois toutes ces versions au début du fichier"

C'est vrai que c'est pas mal, mais un peu trop galere a mettre en place (utilisation de variables globales, ...). Je garde cette idée de coté, mais je ne vais pas la mettre en place pour l'instant (faute de temps un peu aussi).

"en utilisant les classes de Delphi faites pour cela (TReader, TWriter, ...) "
J'etais parti sur l'etude de ces classes au debut, je pensai les utiliser ou m'en inspirer fortement, mais j'ai laissé ca de coté pour voir si je trouvai pas une autre solution ... que j'ai finalement choisie.


En tout cas merci beaucoup pour toutes ces information qui me sont d'une aide precieuse



1 2

Cette discussion est classé dans : champs, classe, structure, objets, serialisation


Répondre à ce message

Sujets en rapport avec ce message

Liste d'objets [ par okemobi ] Bonjour,J'essaie de créer une liste d'objets avec une TList.Hypothèses : - J'ai une classe d'objet TMyClass avec ses propriétés et méthodes, - j' Structure Message [ par MicLau ] Bonjour,Je dois envoyer un message via TCPIP d'un ordinateur à un autre. Mon problème est le suivant :Lorsque je concatène 3 champs, Delphi compresse Image+base de donnée [ par micfrip ] Bon, j'ai une petite question toute bête... ( c'est probablement tellement bête que je ne l'ai trouvé nulle part ).J'ai une base de donnée Access qui BDGRID+image [ par micfrip ] Bon, j'ai une petite question toute bête... ( c'est probablement tellement bête que je ne l'ai trouvé nulle part ).J'ai une base de donnée Access qui Locate dans un champs Memo [ par erijeux ] Je cherche a faire un locate sur un champs memo.je ne trouve pas la solution quoique que je fasse j'ai une erreur.Help !!!!!!!! PLEASE !!!!!!!!!!! Libération de ressources [ par nicolaspennaneach ] j'ai une application MDI où chaque fenètre enfant possède un thread pour effectuer certaines opérations. Lorsque j'exécute la méthode formclose d'une classe indy client / serveur [ par norton ] hello, n'ayant pas la classe TNMUdp je voulais continuer mon prog de jestion de CyberSalle avec le composant indy mais j'aimerai faire ce code en comp ouvrir un fichier texte [ par kwentinn ] salut tout le monde,je dois faire un petit prog sensé récupérer des infos à partir d'un fichier texte.Sur chaque ligne du fichier texte, il y a 6 cham IdTcpServer-Client [ par pfp ] Je cherche depuis un sacré moment le moyen d'employer les objets IdTcpServer-Client pour faire une petite application simple d'envoi et réception de m Ecriture dans un fichier binaire [ par ixpichu ] Salut!!g un petit problème, j'aimerai écrire une structure dans un fichier binaire, je voudrais savoir: -comment je dois déclarer ma structure qui


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,515 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.