I. Introduction:
Memento is a Design Pattern of the Behavior type. It allows us to store and restore the status of an object that does not reveal its inner details. Memento Pattern consists of the following components:
- Originator : An object whose state is stored or restored.
- Mementor: the state (State) of the object when being stored.
- Caretaker: role storage and allocation of Memento. It is responsible for storing States in Memento form and allocating States to Objects as needed.
II. How it works:
Memento Pattern will structure the data to save an Object into a State , then will save this State . The State after being saved will be called the Memento . CareTaker will take the role of storing the State into Memento and export Mementos into State for use. Because the state of objects is stored in the state , when the state is passed through different objects, it will not reveal detailed implementations of those objects.
III. When is Memento Pattern used?
Memento Pattern is used whenever we want to save and then restore the state of an Object. For example, when we play a game, we want to save all the state we played before so that after quitting the game and reopening we can continue playing, then we can apply Memento Pattern to solve this problem.
IV. For example:
In the example, we will use the class Game
with the role of Originator
. Inside the class Game
will contain a State
to save its state. The Game
‘s state will be saved in the UserDefault
, so we need to adopt the Codable
protocol.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token comment">// MARK: - Originator</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Game</span> <span class="token punctuation">:</span> <span class="token builtin">Codable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">State</span> <span class="token punctuation">:</span> <span class="token builtin">Codable</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> attemptsRemaining <span class="token punctuation">:</span> <span class="token builtin">Int</span> <span class="token operator">=</span> <span class="token number">3</span> <span class="token keyword">public</span> <span class="token keyword">var</span> level <span class="token punctuation">:</span> <span class="token builtin">Int</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">public</span> <span class="token keyword">var</span> score <span class="token punctuation">:</span> <span class="token builtin">Int</span> <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">var</span> state <span class="token operator">=</span> <span class="token function">State</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">rackUpMassivePoints</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token punctuation">.</span> score <span class="token operator">+</span> <span class="token operator">=</span> <span class="token number">9002</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">monstersEatPlayer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token punctuation">.</span> attemptsRemaining <span class="token operator">-</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The Game
Object will be saved to the UserDefault
as a Data
. Therefore, Data
in Swift in this example will act as Memento.
1 2 3 | <span class="token comment">// MARK: - Memento</span> <span class="token keyword">typealias</span> <span class="token builtin">GameMemento</span> <span class="token operator">=</span> <span class="token builtin">Data</span> |
To manage the storage of Mementos, we will need CareTaker. GameSystem
acts as CareTaker. This class will be responsible for storing or retrieving Mementos when necessary.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token comment">// MARK: - CareTaker</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">GameSystem</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">let</span> decoder <span class="token operator">=</span> <span class="token function">JSONDecoder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">let</span> encoder <span class="token operator">=</span> <span class="token function">JSONEncoder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">let</span> userDefaults <span class="token operator">=</span> <span class="token builtin">UserDefaults</span> <span class="token punctuation">.</span> standard <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token number">_</span> game <span class="token punctuation">:</span> <span class="token builtin">Game</span> <span class="token punctuation">,</span> title <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> encoder <span class="token punctuation">.</span> <span class="token function">encode</span> <span class="token punctuation">(</span> game <span class="token punctuation">)</span> userDefaults <span class="token punctuation">.</span> <span class="token keyword">set</span> <span class="token punctuation">(</span> data <span class="token punctuation">,</span> forKey <span class="token punctuation">:</span> title <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">load</span> <span class="token punctuation">(</span> title <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Game</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> userDefaults <span class="token punctuation">.</span> <span class="token function">data</span> <span class="token punctuation">(</span> forKey <span class="token punctuation">:</span> title <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token keyword">let</span> game <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token operator">?</span> decoder <span class="token punctuation">.</span> <span class="token function">decode</span> <span class="token punctuation">(</span> <span class="token builtin">Game</span> <span class="token punctuation">.</span> <span class="token keyword">self</span> <span class="token punctuation">,</span> from <span class="token punctuation">:</span> data <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Error</span> <span class="token punctuation">.</span> gameNotFound <span class="token punctuation">}</span> <span class="token keyword">return</span> game <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">enum</span> <span class="token builtin">Error</span> <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">,</span> <span class="token builtin">Swift</span> <span class="token punctuation">.</span> <span class="token builtin">Error</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> gameNotFound <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">var</span> game <span class="token operator">=</span> <span class="token function">Game</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> game <span class="token punctuation">.</span> <span class="token function">monstersEatPlayer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> game <span class="token punctuation">.</span> <span class="token function">rackUpMassivePoints</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// Save Game</span> <span class="token keyword">let</span> gameSystem <span class="token operator">=</span> <span class="token function">GameSystem</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">try</span> gameSystem <span class="token punctuation">.</span> <span class="token function">save</span> <span class="token punctuation">(</span> game <span class="token punctuation">,</span> title <span class="token punctuation">:</span> <span class="token string">"Best Game Ever"</span> <span class="token punctuation">)</span> <span class="token comment">// New Game</span> game <span class="token operator">=</span> <span class="token function">Game</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string">"New Game Score: <span class="token interpolation"><span class="token delimiter variable">(</span> game <span class="token punctuation">.</span> state <span class="token punctuation">.</span> score <span class="token delimiter variable">)</span></span> "</span> <span class="token punctuation">)</span> <span class="token comment">// Load Game</span> game <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token operator">?</span> gameSystem <span class="token punctuation">.</span> <span class="token function">load</span> <span class="token punctuation">(</span> title <span class="token punctuation">:</span> <span class="token string">"Best Game Ever"</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string">"Loaded Game Score: <span class="token interpolation"><span class="token delimiter variable">(</span> game <span class="token operator">?</span> <span class="token punctuation">.</span> state <span class="token punctuation">.</span> score <span class="token delimiter variable">)</span></span> "</span> <span class="token punctuation">)</span> |
V. References:
- Design Pattern by Tutorials – Raywenderlich
- State Pattern by refactoring.guru