Chcesz tu zrobić coś co się nazywa lazy loading. Zakładam, że nie używasz żadnego ORMa, więc poza powyższymi sugestiami możesz spróbować jeszcze czegoś w ten deseń:
class DbObject
{
public:
int GetId() const;
bool IsLoaded() const;
void SetLoaded();
void EnsureIsLoaded()
{
if(!IsLoaded())
{
//tutaj w magiczny sposób odczytujesz sobie klasę
SetLoaded();
}
}
}
class CarEngine: public DbObject
{
public:
int GetBlabla()
{
EnsureIsLoaded();
return m_blabla;
}
private:
int m_blabla;
}
class Car: public DbObject
{
CarEngine m_engine;
}
Piszę to z palca, więc coś może nie działać do końca.
Zasada jest prosta. Tworzysz sobie klasę bazową, która zwraca Ci ID i określa, czy obiekt został w pełni odczytany. Potem tworzysz sobie klasy potomne (CarEngine) ze wszystkim co potrzebujesz mieć w danej klasie. Ważne, żeby w getterach i setterach umieścić EnsureIsLoaded.
Jak to ma działać?
ZAWSZE odczytujesz tylko Id. W momencie kiedy próbujesz użyć tego obiektu, obiekt najpierw sam się odczyta w pełni, a dopiero potem zwróci Ci wartość. Np:
SELECT * FROM cars
Zwraca Ci coś takiego:
ID |
CarName |
EngineId |
1 |
Skoda |
2 |
2 |
Fiat |
8 |
std::vector<Cars *> cars;
ReadCars(cars);
Car * pCar = cars[0];
int engineId = pCar->GetEngine()->GetId(); // tu bierzesz Id silnika. Nic się nie dzieje
int engineBlabla = pCar->GetEngine()->GetBlabla(); //tu dzieje się cała magia. Najpierw dzięki EnsureIsLoaded odczytuje się cały silnik, a na koniec zwraca Ci odpowiednie pole.
To taka podstawa do bardzo prymitywnego ORMa z LazyLoading. Pokazuję to jako jedną z możliwości. W C# zdecydowanie prościej coś takiego napisać ze względu na refleksje, w C++ będzie trudniej i trzeba pamiętać o EnsureIsLoaded. Chociaż pewnie dałoby się to jakoś mądrze ominąć.