I have been trying to come up with a method to "serialize" various objects into various different formats. For example:
class Shape {
public:
virtual std::string_view name() const = 0;
virtual double area() const = 0;
};
class Square : public Shape {
...
};
class Triangle: public Shape {
...
};
Suppose I have the two types of shapes above. Now, I want to be able to serialize (and eventually deserialize) these different class into different formats (e.g., string, JSON, bytes, ...)
The first solution is to perform the serialization and deserialization within the class itself (this is what overriding the insertion operator does). However, if I start adding different types of serialization, I have to modify every single Shape class.
class Shape {
public:
...
virtual std::string serializeToString() const = 0;
virtual json_object serializeToJSON() const = 0;
//Repeat for every type of serialized output...
};
The second solution I found was to use the visitor pattern. Using that pattern, I can create a different visitor for each type of serialized format. And, since I have much less serialized formats than visitors, I suppose it is acceptable that you have to modify every visitor class when a new Shape class is added.
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;
};
But of course, the problem with the visitor pattern is the visitors have no way to access the private data of each class. And since this is serialization I am talking about, I have to access every single private data member which I cannot see how to do without breaking encapsulation of the shapes completely.
A third option I thought of is just using some form of templates and template specialization to choose the correct function for serialization based upon the format and class. The problem is, this doesn't work at runtime on a generic Shape instance...
So my questions are:
- Is there a modification to the visitor pattern which overcomes the private data problem?
- Is there an alternative to the visitor pattern that would ideally not require updating a the same classes over and over?
- Whatever method I use, is there ay way to make the process as "reversible" as possible (e.g, for deserialization)?
friend
gets you access to the private methods?friend
to get access to private members.Shape
(for example the JSON one), you could use that to serialize into different formats through a converter class. This would remove the need to modify yourShape
classes whenever you add a new serialization format. Instead, you'd add a new converter. This would work for serialization and deserialization the same.