J'ai essayé de créer une méthode pour "sérialiser" divers objets dans divers formats différents. Par example:
class Shape {
public:
virtual std::string_view name() const = 0;
virtual double area() const = 0;
};
class Square : public Shape {
...
};
class Triangle: public Shape {
...
};
Supposons que j'ai les deux types de formes ci-dessus. Maintenant, je veux être capable de sérialiser (et éventuellement de désérialiser) ces différentes classes dans différents formats (par exemple, chaîne, Json, octets, ...)
La première solution consiste à effectuer la sérialisation et la désérialisation dans la classe elle-même (c'est ce que le remplacement de l'opérateur d'insertion fait). Cependant, si je commence à ajouter différents types de sérialisation, je dois modifier chaque classe de forme.
class Shape {
public:
...
virtual std::string serializeToString() const = 0;
virtual json_object serializeToJSON() const = 0;
//Repeat for every type of serialized output...
};
La deuxième solution que j'ai trouvée était d'utiliser le motif de visiteur. En utilisant ce motif, je peux créer un visiteur différent pour chaque type de format sérialisé. Et, étant donné que j'ai beaucoup moins de formats sérialisés que les visiteurs, je suppose qu'il est acceptable de modifier chaque classe de visiteur lorsqu'une nouvelle classe de forme est ajoutée.
class Shape {
public:
virtual void accept(ShapeVisitor& v) = 0;
};
class Square : public Shape;
class Triangle : public Square;
class ShapeVisitor {
public:
virtual void visit(const Square& s) = 0;
virtual void visit(const Triangle& s) = 0;
};
class StringShapeVisitor : public ShapeVisitor {
public:
void visit(const Square& s) const;
void visit(const Triangle& s) const;
};
Mais bien sûr, le problème avec le modèle de visiteur est que les visiteurs n'ont aucun moyen d'accéder aux données privées de chaque classe. Et comme c'est la sérialisation, je parle de ce que je dois accéder à tous les membres de données privés que je ne peux pas voir comment faire de l'encapsulation des formes complètement.
Une troisième option que j'ai pensé à utiliser simplement une forme de modèles et de spécialisation de modèles pour choisir la fonction correcte pour la sérialisation en fonction du format et de la classe. Le problème est que cela ne fonctionne pas au moment de l'exécution d'une instance de forme générique ...
Donc, mes questions sont:
Un bon démarrage est d'éviter de réinventer la roue et de regarder Boost Serialization Bibliothèque . Même si vous ne pouvez pas utiliser la bibliothèque, sa conception vaut la peine d'être étudiée.
Votre Première solution a un inconvénient: vous prévoyez autant de fonctions qu'il existe des formats. Votre classe souffre donc d'un manque de séparation des préoccupations, a plus d'une raison de modifier (SRP), et de plus en plus n'est pas vraiment ouverte à l'extension car chaque nouveau format nécessite une modification.
Une meilleure variante de cette approche résout celle-ci avec un objet de sérialisation (archive/json Fichier/Stream/XML String/....): Les fonctions de sérialisation dans les objets sérialisés effectuent les opérations de sérialisation primitives avec cet objet. Vous pouvez utiliser une approche de modèle, mais vous pouvez également concevoir des classes de sérialisation avec des spécialisations de format.
Dans la deuxième solution , le visiteur est un autre type d'intermédiaire. Mais au lieu de l'objet sérialisé racontant des primitives de sérialisation, il appartient au visiteur de demander les données et de faire économiser. Cela est dû au fait que le visiteur est destiné à un grand spectre d'algorithmes. De plus, vous avez la question de la vie privée. Vous pourriez bien sûr rendre l'ami des visiteurs, mais le visiteur de la sérialisation compterait complètement sur les internes d'un autre objet, ce qui est contraire à la loi de Demeter.