Save Checkpoints & Restore Without Exposing Private State
Exposing public getters and setters for every private HP, mana, position, and inventory field breaks encapsulation completely. Any external script can tamper with internal health invariants, and whenever new fields (like cooldowns or curses) are added to `Hero`, external save structures break.
To allow external saving, Hero must make its internal fields public or provide public setters. Any external script can tamper with hero.hp = 99999 or bypass health bounds.
Capture and externalize an object's internal state without violating encapsulation, so that the object can be restored to this state later.
Hero (the Originator) creates an instance of HeroSnapshot (the Memento). HeroSnapshot keeps its fields and constructor private, declaring friend class Hero; so that only Hero can read and write its contents. CheckpointHistory (the Caretaker) holds snapshots as opaque black boxes, managing undo/restore stacks without ever inspecting or tampering with internal state.
In Sprint 2 (C2 Β· Composite), we designed a recursive inventory hierarchy where pouches and bags nest seamlessly to arbitrary depths. Equipped with their stacked buffs and organized inventories, heroes now face deadly dungeon boss chambers where total party wipes are frequent.
We need a boss-room checkpoint save-and-restore system. When a player activates a checkpoint shrine before entering a boss room, the engine must capture Hero's current state (HP, mana, position (x, y)) and restore it seamlessly if the party dies during combat.
Exposing public getters and setters for every private HP, mana, position, and inventory field breaks encapsulation completely. Any external script can tamper with internal health invariants, and whenever new fields (like cooldowns or curses) are added to Hero, external save structures break.
The Memento Pattern (HeroSnapshot) captures an opaque state snapshot managed by a SaveManager caretaker (CheckpointHistory), keeping internals strictly private via friendship. Next in Sprint 4 (C4 Β· Prototype), where entering the dungeon requires spawning waves of pre-configured monster variants (Elite Goblin, Enraged Orc) configured at runtime.
The product ticket
Before confronting a deadly dungeon boss, players hit 'Save Checkpoint'. If the party wipes out, the game rolls back to the saved state: restoring HP, mana, and dungeon position (x, y). The save system must support saving multiple checkpoints across an adventure and restoring previous checkpoints in LIFO order.
- β’
Capture the internal state of
Heroat any given moment into an opaque checkpoint token (HeroSnapshot). - β’
Restore
Herocleanly from a snapshot, ensuring internal invariants (hp <= maxHp) remain intact. - β’
Manage a history of saved checkpoints in a caretaker manager (
CheckpointHistory) supporting push and pop operations. - β’
Strictly preserve encapsulation: external classes must not be able to read, mutate, or tamper with the snapshot's internal state.
Next sprint, developers will add status curses, skill cooldowns, and temporary equipment buffs to Hero. Adding new fields to Hero must not break the checkpoint manager or require exposing new public getters and setters.
Step 1 β The naive code & why it breaks
The naive SaveSystem accesses public fields of Hero directly (hero.hp_, hero.mana_, hero.x_, hero.y_) or uses broad public setters to copy data into an external struct.
#include <print>
#include <string>
// Naive: Hero exposes all internal fields publicly so SaveSystem can copy them!
class NaiveHero {
public:
std::string name;
int hp{100};
int maxHp{100};
int mana{50};
int x{0};
int y{0};
void takeDamage(int dmg) {
hp = std::max(0, hp - dmg);
}
};
// External save system reaches straight into Hero's guts!
struct NaiveSaveData {
int hp;
int mana;
int x;
int y;
};
class NaiveSaveSystem {
public:
NaiveSaveData createSave(const NaiveHero& h) {
// Exposes every private detail to external code
return {h.hp, h.mana, h.x, h.y};
}
void restore(NaiveHero& h, const NaiveSaveData& data) {
h.hp = data.hp;
h.mana = data.mana;
h.x = data.x;
h.y = data.y;
}
};
int main() {
NaiveHero hero{"Sir Alistair"};
NaiveSaveSystem saveSys;
// Save checkpoint before boss fight
NaiveSaveData checkpoint = saveSys.createSave(hero);
std::println("[Before Boss] {} HP: {}, Mana: {} at ({}, {})",
hero.name, hero.hp, hero.mana, hero.x, hero.y);
// Boss fight: player takes massive damage
hero.takeDamage(85);
std::println("[During Boss] {} HP: {} (Critical!)", hero.name, hero.hp);
// Flaw: any external code can tamper with the save data!
checkpoint.hp = 99999; // Corrupted save!
// Restore corrupted state
saveSys.restore(hero, checkpoint);
std::println("[After Restore] {} HP: {} (Invariants destroyed!)", hero.name, hero.hp);
}To allow external saving, Hero must make its internal fields public or provide public setters. Any external script can tamper with hero.hp = 99999 or bypass health bounds.
Whenever Hero adds or renames an internal field (e.g. mana, statusEffects), SaveSystem and its save struct break and must be manually rewritten.
Because save data is stored in open structs, external code or rogue systems can mutate fields between save and restore, corrupting game state.
Step 2 β The fix: Memento Pattern
Capture and externalize an object's internal state without violating encapsulation, so that the object can be restored to this state later.
Hero (the Originator) creates an instance of HeroSnapshot (the Memento). HeroSnapshot keeps its fields and constructor private, declaring friend class Hero; so that only Hero can read and write its contents. CheckpointHistory (the Caretaker) holds snapshots as opaque black boxes, managing undo/restore stacks without ever inspecting or tampering with internal state.
Hero β holds state (HP, mana, position) and invariants; creates snapshots and restores its state from them.
HeroSnapshot β holds private state fields; grants access only to Hero via friend class Hero;.
CheckpointHistory β manages a stack of snapshots; never inspects or alters the internal data.
Unlock Full UML Diagram & C++23 Solution
The interactive UML architecture diagrams, refactored C++23 code, real-world extensions, and interview Q&A are part of Brute2Best Pro. All 7 foundation chapters are 100% free.
First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.