When you're writing an application that needs to read and work with two versions of data in the same way, what is the best way to structure your classes to represent that data. I have come up with three scenarios:
- Common Base/Specific Children
- Data Union
- Distinct Structures
Version 1 Car Example
byte DoorCount
int Color
byte HasMoonroof
byte HasSpoiler
float EngineSize
byte CylinderCount
Version 2 Car
byte DoorCount
int Color
enum:int MoonRoofType
enum:int TrunkAccessories
enum:int EngineType
Common Base/Specific Children
With this method, there is a base class of common fields between the two versions of data and a child class for each version of the data.
class Car {
byte DoorCount;
int Color;
}
class CarVersion1 : Car {
byte HasMoonroof;
byte HasSpoiler;
float EngineSize;
byte CylinderCount;
}
class CarVersion2 : Car {
int MoonRoofType;
int TrunkAccessories;
int EngineType;
}
Strengths
- OOP Paradigm
Weaknesses
- Existing child classes will have to change if a new version is released that removes a common field
- Data for one conceptual unit is split between two definitions not because of any division meaningful to itself.
Data Union
Here, a Car is defined as the union of the fields of Car across all versions of the data.
class Car {
CarVersion version;
byte DoorCount;
int Color;
int MoonRoofType; //boolean if Version 1
int TrunkAccessories; //boolean if Version 1
int EngineType; //CylinderCount if Version 1
float EngineSize; //Not used if Version2
}
Strengths
- Um... Everything is in one place.
Weaknesses
- Forced case driven code.
- Difficult to maintain when another version is release or legacy is removed.
- Difficult to conceptualize. The meanings of the fields changed based on the version.
Distinct Structures
Here the structures have no OOP relationship to each other. However, interfaces may be implemented by both classes if/when the code expects to treat them in the same fashion.
class CarVersion1 {
byte DoorCount;
int Color;
byte HasMoonroof;
byte HasSpoiler;
float EngineSize;
byte CylinderCount;
}
class CarVersion2 {
byte DoorCount;
int Color;
int MoonRoofType;
int TrunkAccessories;
int EngineType;
}
Strengths
- Straightforward approach
- Easy to maintain if a new version is added or legacy is removed.
Weaknesses
- It's an anti-pattern.
Is there a better way that I didn't think of? It's probably obvious that I favor the last methodology, but is the first one better?