2010-11-19 27 views
2

Dans le produit que je travaille, un scénario très basique est sérialisation des classes. Typiquement, une classe devant être sérialisée appelle une sérialisation sur son sous-composantpour la délégation aux sous-composante

par exemple. s'il y a une classe s.t. classe A {B, C, D;} puis A.Pack appellera paquet fonction sur B, C, D.

Comme il existe de nombreuses classes de ce type, le même modèle de code doit être dupliqué encore et encore. Est-il possible d'encapsuler ce comportement dans un canevas (éventuellement en utilisant des gabarits et l'héritage)

+1

Si vous voulez dire "est-ce que je peux écrire un modèle pour appeler automatiquement une méthode sur chacune de mes variables membres?", Alors la réponse est non ... –

+0

Au meilleur de ma connaissance vous avez raison, mais je suis espérant contre quelqu'un d'espoir a une meilleure idée :-) – Gaurav

Répondre

2

La manière habituelle de faire un modèle faire est d'utiliser une liste de type:

#include <iostream> 

// typelist definition 
struct Empty {}; 

template < typename H, typename T = Empty > 
struct Cons { 
    typedef H head; 
    typedef T tail; 
}; 

// interfaces all items support 
class IPack 
{ 
public: 
    virtual void Pack() = 0; 
}; 

// some packable items 
class Fee : public IPack 
{ 
public: 
    virtual void Pack() { 
     std::cout << "Packed Fee\n"; 
    } 
}; 

class Fi : public IPack 
{ 
public: 
    virtual void Pack() { 
     std::cout << "Packed Fi\n"; 
    } 
}; 

class Fo : public IPack 
{ 
public: 
    virtual void Pack() { 
     std::cout << "Packed Fo\n"; 
    } 
}; 

class Fum : public IPack 
{ 
public: 
    virtual void Pack() { 
     std::cout << "Packed Fum\n"; 
    } 
}; 

// these two templates create a composite IPack from a list 
// of the types of its parts 
template <typename Types> 
class PackList : public PackList<typename Types::tail> 
{ 
protected: 
    typedef typename Types::head Item; 
    Item item; 

public: 
    virtual void Pack() { 
     item.Pack(); 
     PackList<typename Types::tail>::Pack(); 
    } 
}; 

template <> 
class PackList<Empty> : public IPack 
{ 
public: 
    virtual void Pack() {} 
}; 

// FeeFiFoFum is a composite of four items 
class FeeFiFoFum : public PackList<Cons<Fee,Cons<Fi,Cons<Fo,Cons<Fum> > > > > 
{ 
}; 

// create a FeeFiFoFum and call pack on it, which calls pack on its parts 
int main() 
{ 
    FeeFiFoFum giant; 

    giant.Pack(); 
} 

implémentations appropriées des composites créés à partir des listes de type vous donnent accesseurs pour les membres et ainsi de suite, mais cela suffit pour montrer comment ils fonctionne, et imprime qu'il emballé frais, Fi, Fo et Fum sans spécifier le comportement.

+0

Génial! Modèles à la rescousse à nouveau. Merci :-) – Gaurav

0

Il est possible que le canevas de visiteur puisse aider.

http://en.wikipedia.org/wiki/Visitor_pattern

L'idée de c'est de séparer la logique de traversal (pas à pas dans vos objets) lors de la manipulation de chaque objet. Dans ce cas, la logique par objet est la sérialisation (encodage) d'un seul objet (ou désérialisation, bien sûr). Cela devrait être assez simple et peu répétitif en utilisant des techniques de POO normales.

Mise en œuvre du traversal et le code spécifique visiteur-modèle est ennuyeux, mais il est la plupart du temps et passe-partout doit être une chose unique.

2

Une conception possible qui pourrait aider à accomplir est d'utiliser le Composite pattern. Votre composant (pour emprunter le dessin Wikipedia) est Packable, ce qui mettrait en œuvre un pack Template Method() qui peut faire quelque chose comme ceci:

GetChildren(); 
    for each child: 
     child.Pack() 
PackImpl(); 

PackImpl() est une méthode virtuelle pure dans Packable, et toutes les classes qui héritent l'implémentent correctement. GetChildren() retournera un conteneur STL (éventuellement vide), pour l'itération. Il peut être implémenté dans Packable, avec une collection de membre privé pour stocker les objets enfants. Fondamentalement, vous héritez alors toutes les classes de Packable, mettre en œuvre PackImpl(), et vous avez terminé.

Notez que cela entraînera des problèmes si votre hiérarchie d'héritage dépend des pièces de l'enfant étant directement membres. Si vous avez abordé le problème en termes d'agrégation, cela devrait fonctionner correctement.

+0

Je sais que je suis ici, mais être gourmand est pas là someway pour éviter réimplémentant chaque GetChildren car le comportement est le même à chaque fois (ofcos les enfants exacts diffèrent). Sinon, semble une bonne soln. – Gaurav

+0

Modifié - Je n'ai pas décrit Composite correctement, et cela répond à vos préoccupations. –

0

Un intervenant a écrit:

Si vous voulez dire « est-il un moyen que je peux écrire un modèle pour appeler automatiquement une méthode sur chacun de mes variables membres? », La réponse est non ...

Mon (un peu mal) à l'encontre de ce qui est oui, si la méthode est la destructor ...

#include <iostream> 
using namespace std; 

bool Enable = false; 

template <typename T> 
class DS : public T { 
public: 
    ~DS() { 
     if (Enable) T::Serialize(); 
    } 
}; 

class A { 
protected: 
    void Serialize() { cout << "A" << endl; } 
}; 

class B { 
protected: 
    void Serialize() { cout << "B" << endl; } 
}; 

typedef DS<A> DSA; 
typedef DS<B> DSB; 

class C { 
protected: 
    void Serialize() { cout << "C" << endl; } 
private: 
    DSA a; 
    DSB b; 
}; 

typedef DS<C> DSC; 

int 
main() 
{ 
    DSC c; 
    { 
     DSC c_copy = c; 
     Enable = true; 
    } 
    Enable = false; 
} 

le La sortie est dans l'ordre inverse, donc pour reconstruire les objets, vous devez analyser les données sérialisées et pousser chaque objet terminé sur une pile. Les objets composites sauraient alors combien d'enfants sortiraient de la pile. Ou, bien sûr, la sérialisation pourrait aller à une structure intermédiaire.Une autre idée intrigante serait d'utiliser ce hack une fois au démarrage (créer et détruire un seul objet spécial) où les rappels des destructeurs créeraient une structure de données qui décrivait l'objet original.

Je note également que les constructeurs de copie implicites ont un potentiel d'abus similaires, et possible pour l'avant ...