Table des matières du Kit de Support | Index du Kit de Support |
Ne dérive d'aucune classe
Déclarée dans : be/support/Archivable.h
BArchivable est un protocole pour archiver et restaurer des objets. Quand vous archivez un objet, vous enregristrez son état dans un BMessage qui peut être envoyé à une autre application, linéarisé et sauvé dans un fichier, caché en mémoire, etc. Restaurer fait l'inverse : on restaure l'état d'un objet à partir d'une archive (BMessage).
Pour archiver un objet, vous créez un BMessage et le passez à la méthode Archive() de l'objet:
BMessage message; theObject->Archive(&message);
C'est le rôle de l'implémentation de la méthode Archive() que d'écrire une représentation de l'objet dans le message. Chaque classe est responsable de l'archivage des parties propres à l'objet qu'elle définit. Pour incorporer des propriétés archivées par les classes ascendantes, l'implémentation de la méthode doit commencer par un appel à la version de Archive() dont elle hérite de ses classes ascendantes.
La chaîne des appels aux méthodes héritées se termine à la classe racine BArchivable. Sa version de Archive() place le nom de la classe de l'objet dans l'archive sous le nom de champ "class". Cette information est utilisée plus tard lors de l'instantiation d'objets à partir d'une archive. Il est d'usage pour les classes dérivess de placer leur propre nom de classe dans le tableau "class", pourvu que les instances de ces classes peuvent être initialisées à partir d'une archive. Une classe abstraite ne devrait pas placer son nom dans le tableau. (Voir validate_instantiation() pour plus d'informations sur ce sujet.)
Si une classe n'a aucune donnée à ajouter à l'archive BMessage, il n'est nul besoin d'implémenter une méthode Archive() ; elle peut réutiliser la version héritée. Cependant, elle doit implémenter le constructeur et la méthode statique Instantiate() pour que les objets soient correctement désarchiver.
Le second argument de Archive(), un drapeau booléen, indique si l'archive doit être profonde ou superficielle. Par défaut le drapeau est positionné à true (profond).
Pour une archive profonde, une classe doit inclure dans son archive tous les autres objets qu'elle "possède". Pour une archive superficielle, elle doit exclureces objecs. Par exemple un objet BView archive ses descendants pour un archivage profond, mais pas pour un archivage superficiel.
Pour réaliser un archivage profond, un objet invoque la méthode Archive() sur l'objet courant, et ajoute l'archive résultante à sa propre archive. Par exemple:
status_t TheClass::Archive(BMessage *archive, bool deep) { baseClass::Archive(archive, deep); archive->AddString("class", "TheClass"); . . . if ( deep ) { BMessage cronyArchive; if ( crony->Archive(&cronyArchive, deep) == B_OK ) archive->AddMessage("crony", &cronyArchive); } }
Un archivage, qu'il soit profond ou superficiel, ne doit en aucun cas inclure les objets avec lesquels l'objet à archiver est associé, mais dont il n'est pas propriétaire. Par exemple, une BView n'archive pas son ancêtre ou la BWindow à laquelle elle est attachée.
Les conflits de noms dans une archive ne sont pas automatiquement détectés ni corrigés. Par exemple, si deux classes A et une sous-classe B ajoutent deux champs nommés "chien" à l'archive, le mécanisme de désarchivage sera mis en difficulté.
Pour éviter de tels conflits, toutes les méthodes Archive() implémentées dans les Kits Be utilisent des noms commençant par un tiret souligné ("_name") ou le préfixe "be:" ("be:name" pour le champ "name"). Utilisez une convention différente pour nommer les odnnées archivées dans les classes que vous définissez.
Pour être désarchivable, une classe doit implémenter un constructeur qui prend une archive BMessage en argument. Le constructeur est alors la contrepartie de la méthode Archive() : il commence par appeler le constructeur de sa classe ancêtre immédiate, puis cherche dans le BMessage les champs qui ont été ajoutés par la méthode Archive() .
La classe doit aussi implémenter la méthode statique Instantiate() (déclarée dans BArchivable), qui ne fait rien de plus qu'appeler le constructeur acceptant une archive, et retourne un pointeur sur un objet BArchivable. Par exemple:
BArchivable *TheClass::Instantiate(BMessage *archive) { if ( validate_instantiation(archive, "TheClass")) return new TheClass(archive); return NULL; }
La fonction validate_instantiation(), fournie par le Kit de Support, réalise un test qui assure que l'objet BMessage est bien une archive de la classe dont le nom est fourni en paramètre.
Pour désarchiver à partir d'un BMessage, vous appelez la fonction instantiate_object() . Quand on lui passe une archive BMessage, instantiate_object() récupère le premier nom dans le tableau "class", ainsi que la méthode Instantiate() pour cette classe, et l'invoque. En cas d'échec, on va chercher le nom suivant dans le tableau "class" (remontant donc dans la hiérarchie) et on essaye ; et ainsi de suite.
instantiate_object() renvoie une instance de la classe BArchivable. Vous pouvez alors utiliser cast_as() pour sous-typer l'objet vers une classe plus intéressante. Une session de désarchivable typique ressemble alors à ceci :
/* archive est le BMessage à désarchiver en un objet. * Dans ce cet exemple, il va s'agir d'une BView. */ BArchivable *unarchived = instantiate_object(archive); if ( unarchived ) { BView *view = cast_as(unarchived, BView); if ( view ) { . . . } }
La classe dérivant de BArchivable doit être exportée pour que le désarchivage fonctionne :
class _EXPORT MyArchivableView : public BView { ... static BArchivable *Instantiate(BMessage *data); ... };
Comme décrit ci-dessus, une application peut seulement désarchiver les objets qu'elle connaît—elle ne peut pas désarchiver un objet dont elle n'a pas connaissance du code à exécuter.
Une convention additionnelle ne respecte pas cette restriction : une archive BMessage peut inclure un champ B_STRING_TYPE, nommé "add-on", qui peut contenir la signature d'un add-on aui définit l'objet archivé. Si instantiate_object() échoue à désarchiver un objet du premier coup, elle va charger le code d'un éventuel add-on, puis réessaye.
La manière dont un hôte interagit avec une instance désarchivée d'une classe inconnue n'est pas spécifiée. Il appartient aux programmeuses (et programmeurs ...) de définir des points d'entrée et des protocoles, comme pour n'importe quel module d'extension (add-on).
|
Ne fait rien.
|
Ne fait rien.
|
Les classes dérivées doivent implémenter Instantiate() pour returner une nouvel objet BArchivable qui serait construit à partir d'un BMessage archive. Par exemple:
BArchivable *TheClass::Instantiate(BMessage *archive) { if ( !validate_instantiation(archive, "TheClass") ) return NULL; return new TheClass(archive); }
|
Cette méthode dépend d'un constructeur qui peut initialiserle nouvel objetà partir d'un BMessage archive. Voir ci-dessus "Instantiability" pour de plus amples informations.
L'implémentation par défaut retourne NULL.
|
L'implémentation par défaut ajoute le nom de la classe de l'objet au champ "class" de l'archive. Les classes dérivées doivent surcharger Archive() pour étendre cette implémentation en ajoutant au BMessage les données qui décrivent l'état courant de l'objet. Chaque implémentation de cette méthode doit débuter par un appel à la version héritée :
/* Supposons que MyView hérite de BView. */ status_t MyView::Archive(BMessage *archive, bool deep) { BView::Archive(archive, deep); . . . }
Si la classe peut être instanciée directement à partir d'une classe dérivée, elle doit aussi ajouter son nom au tablau "class" :
archive->AddString("class", "MyView");
Le drapeau deep indique si Archive() doit inclure les objets qui "appartiennent" à l'objet en cours d'archivage. Par exemple, un archivage profond d'une BView incluera les formes archivées des descendants de la vue. Un exemple en est donné ci-dessus, voir "Archivages Profonds et Superficiels".
Archive() doit retourner B_OK si l'opération est réalisée avec succès ; sinon, elle doit retourner B_ERROR ou un code erreur plus descriptif.
Tables des matières du Kit de Support | Index du Kit de Support |
Be, Inc (2000).
Traduction Sylvain Kerjean (2001)