Let's say I want to build a Car
which has components like Motor
, Tire
, etc. which are all derived from a "component" base class. Each component has its own states (i.e. motor has RPM, tire has pressure, and so on) as a subclass. A Car
class stores all component classes.
Now I want the Car
class to have a "save car states" function which returns an object that loops over a vector of all components and holds all states of all components to restore them later. Each component should individually be responsible of storing and restoring its states to the car state object.
The code below is a minimal example of what I came up with. However it has (at least) two ugly parts in the code, which I think should be avoided:
First, I need to downcast a Base class of states, when I want to set the car components back to a previously saved state. Is this a valid case to downcast? I read that this is usually a sign of bad design. Thus, how can I improve this? Did I completely misuse the concept of polymorphism?
Second, I need to get the raw pointer of an unique_ptr to pass the state back to the component and read it there. Seems pretty ugly to me as well. Thanks for your comments!
This is my code:
#include <iostream>
#include <vector>
class StateClass{
};
class component{
public:
virtual std::unique_ptr<StateClass> saveStates() = 0;
virtual void loadStates(StateClass* states) = 0;
};
class Motor:public component
{
public:
Motor(){
mstates.rpm = 6000;
mstates.motorstates::oilLevel = 1.0;
}
struct motorstates: public StateClass{
double rpm;
double oilLevel;
void setStates(){
}
};
std::unique_ptr<StateClass> saveStates(){
std::unique_ptr<StateClass> tmp(new motorstates(mstates));
return tmp;
};
void loadStates(StateClass* states){
motorstates* savedState = static_cast<motorstates*>(states); // <=== should this be avoided??
mstates.rpm = savedState->rpm;
mstates.oilLevel = savedState->oilLevel;
};
// private:
void someMethod1();
void someMethod2();
motorstates mstates;
};
class Car{
public:
Car(){
listOfComponents.push_back(&amotor);
}
std::vector<component*> listOfComponents;
// private:
Motor amotor;
std::vector<std::unique_ptr<StateClass>> saveState(){
std::vector<std::unique_ptr<StateClass> > states;
for(auto comp : listOfComponents){
states.push_back(comp->saveStates());
}
return states;
}
void loadState(std::vector<std::unique_ptr<StateClass>>& savedStates){
int cntstates = 0;
for(auto comp: listOfComponents){
comp->loadStates(savedStates.at(cntstates++).get()); // <=== this seems pretty ugly
};
}
};
int main()
{
Car acar;
std::cout << "Car rpm: " << acar.amotor.mstates.rpm << std::endl;
std::cout << "Saving states..." << std::endl;
std::vector<std::unique_ptr<StateClass>> savedState = acar.saveState();
std::cout << "Changing car rpm..." << std::endl;
acar.amotor.mstates.rpm = 5000;
std::cout << "Current car rpm: " << acar.amotor.mstates.rpm << std::endl;
StateClass* tmpState = savedState.at(0).get();
Motor::motorstates* tmpMotorstates = static_cast<Motor::motorstates*>(tmpState);
std::cout << "Saved rpm: " << tmpMotorstates->rpm << std::endl;
std::cout << "Loading state... " << std::endl;
acar.loadState(savedState);
std::cout << "Current car rpm: " << acar.amotor.mstates.rpm << std::endl;
}
Additional remarks (see comments):
- I read about visitor classes, but they seem to complicate things more than they would help to write readable code.
- I actually only need to save states during runtime, not to a file
- The car was just an example. My real project uses lots of component classes with hundreds of states. Also some states are supposed to be restored, others not.