void Read_Obj( File *f, Obj *o ) {
if( f->version() == File::Version1 ) {
The if
is so to say a hidden switch/case. And switch/case in C++ is generally interchangeable with polymorphism. Example:
struct Reader {
virtual void Read_Obj( File *f, Obj *o ) = 0;
/* methods to read further objects */
}
struct ReaderV1 : public Reader {
void Read_Obj( File *f, Obj *o ) { /* ... */ };
/* methods to read further objects */
}
struct ReaderV2 : public Reader {
void Read_Obj( File *f, Obj *o ) { /* ... */ };
/* methods to read further objects */
}
And then instantiate the appropriate Reader descendant after opening the file and detecting the version number. That way you would have only one file version check in the top level code, instead of polluting all of the low-level code with the checks.
If code is common between the file version, for convenience you can also put it into the base reader class.
I would strongly advise against the variant with class Obj_v1
and class Obj
where the read()
method belongs to the Obj
itself. This way one easily end-up with circular dependencies and also it is a bad idea to make an object aware of its persistent presentation. IME (in my experience) it is better to have the 3rd party reader class hierarchy responsible for that. (As in the std::iostream
vs. std::string
vs. operator <<
: stream doesn't know string, string doesn't know stream, only the opeartor <<
knows both.)
Otherwise, I personally do not see any big difference between your "Strategy 1" and "Strategy 2". They both use the convert_to()
what I personally think is superficial. IME solution with the polymorphism should be used instead - automatically converting everything to the up-to-date version of the object class Obj
, without the intermediate class Obj_v1
and class Obj_v2
. Since with polymorphism you would have a dedicated read function for every version, ensuring proper object recreation from the read information is easy.
Are there any other patterns that do a better job at this? The ones of you that had some experience with my proposals, what do you think of my worries on the above implementations? Which are preferable solutions?
This is precisely what polymorphism was intended to address and how I generally do such tasks myself.
This is related to object serialization, but I have not seen a single serialization framework (my info is likely outdated) which was capable of supporting several version of the same class.
I personally did end up several times with the following serialization/deserialization class hierarchy:
- abstract reader interface (very slim by definition)
- utility classes implementing the reading and writing of the actual objects from/to the streams (fat, highly reusable code, was used for network transfers too)
- versioned implementations of the reader interface (relatively slim, reuse the fat utility classes)
- writer interface/class (I was always writing up-to-date version of the file. Versioning was using only during reading.)
Hope that helps.