Difficulty: Beginner | Easy | Normal | Challenging
Xcode 14.0.1
Swift 5.7
1. About
In this article, I will introduce to you a Fundamental Design Pattern – Memento Pattern. So what is the Memento Pattern like, its application and how to install it?
As usual, I won’t talk about theory/definition right away. I will show an example of Memento Pattern first, then we will dive into its essence!!!
2. Make a problem
I have a piece of code like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">class</span> <span class="token class-name">BankAccount</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token punctuation">(</span> <span class="token keyword">set</span> <span class="token punctuation">)</span> <span class="token keyword">var</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token operator">=</span> <span class="token number">0.0</span> <span class="token keyword">func</span> <span class="token function-definition function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> balance <span class="token operator">=</span> balance <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">BankCustomer</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> account <span class="token punctuation">:</span> <span class="token class-name">BankAccount</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> account <span class="token punctuation">:</span> <span class="token class-name">BankAccount</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> account <span class="token operator">=</span> account <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">withdraw</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> account <span class="token punctuation">.</span> <span class="token function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> account <span class="token punctuation">.</span> balance <span class="token operator">-</span> amount <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">deposit</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> account <span class="token punctuation">.</span> <span class="token function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> account <span class="token punctuation">.</span> balance <span class="token operator">+</span> amount <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- In the above code, I have 2 classes
BankCustomer
andBankAccount
. Each customer will have a separate bank account and balance. Easy to understand, right? - When we withdraw money, withdraw(amount:) function will be called.
- Everything looks fine? So what’s the problem here?
- Suppose I want to transfer money to a relative, after the system calls withdraw(amount:) and my balance has been deducted. However, I entered the wrong account number of a relative, from here on the system must return the amount I sent to you, right? So the code above has not been able to restore my balance yet
- Or suppose I only have 50 dong in my account but I want to withdraw up to 100 dong. After the system deducts 100 dong, my balance is not enough. So the withdrawal transaction of 100 VND is not valid, so the system needs to restore the amount of 50 VND to his account.
=> That’s also why we have Memento Pattern to help restore the previous state of an object and still not violate the encapsulation principle.
=> Let’s apply Memento Pattern to the example code above
3. Example of Memento Pattern
Following is an example of Memento Pattern of 2 classes BankCustomer
and BankAccount
:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <span class="token keyword">protocol</span> <span class="token class-name">Originator</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Memento</span> <span class="token keyword">func</span> <span class="token function-definition function">restore</span> <span class="token punctuation">(</span> memento <span class="token punctuation">:</span> <span class="token class-name">Memento</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">BankAccount</span> <span class="token punctuation">:</span> <span class="token class-name">Originator</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token punctuation">(</span> <span class="token keyword">set</span> <span class="token punctuation">)</span> <span class="token keyword">var</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token operator">=</span> <span class="token number">0.0</span> <span class="token keyword">func</span> <span class="token function-definition function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> balance <span class="token operator">=</span> balance <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">save</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token class-name">Memento</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">Memento</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> balance <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">restore</span> <span class="token punctuation">(</span> memento <span class="token punctuation">:</span> <span class="token class-name">Memento</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> balance <span class="token operator">=</span> memento <span class="token punctuation">.</span> balance <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Memento</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> balance <span class="token operator">=</span> balance <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">BankCustomer</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> account <span class="token punctuation">:</span> <span class="token class-name">BankAccount</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> account <span class="token punctuation">:</span> <span class="token class-name">BankAccount</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> account <span class="token operator">=</span> account <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">withdraw</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> memento <span class="token operator">=</span> account <span class="token punctuation">.</span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> account <span class="token punctuation">.</span> <span class="token function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> account <span class="token punctuation">.</span> balance <span class="token operator">-</span> amount <span class="token punctuation">)</span> <span class="token keyword">if</span> account <span class="token punctuation">.</span> balance <span class="token operator"><</span> <span class="token number">0</span> <span class="token punctuation">{</span> account <span class="token punctuation">.</span> <span class="token function">restore</span> <span class="token punctuation">(</span> memento <span class="token punctuation">:</span> memento <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Insufficient funds."</span></span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function-definition function">deposit</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token class-name">Double</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> account <span class="token punctuation">.</span> <span class="token function">setBalance</span> <span class="token punctuation">(</span> balance <span class="token punctuation">:</span> account <span class="token punctuation">.</span> balance <span class="token operator">+</span> amount <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- It can be a bit confusing, isn’t it, people. But don’t worry, we will learn each component in the Memento Pattern shortly.
- Before that, try running the code that uses the Memento Pattern first.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">let</span> account <span class="token operator">=</span> <span class="token class-name">BankAccount</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> customer <span class="token operator">=</span> <span class="token class-name">BankCustomer</span> <span class="token punctuation">(</span> account <span class="token punctuation">:</span> account <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Balance: </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">customer <span class="token punctuation">.</span> account <span class="token punctuation">.</span> balance</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Balance: 0.0</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Deposit 100"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Deposit 100</span> customer <span class="token punctuation">.</span> <span class="token function">deposit</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Balance: </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">customer <span class="token punctuation">.</span> account <span class="token punctuation">.</span> balance</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Balance: 100.0</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Withdraw 50"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Withdraw 50</span> customer <span class="token punctuation">.</span> <span class="token function">withdraw</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token number">50</span> <span class="token punctuation">)</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Balance: </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">customer <span class="token punctuation">.</span> account <span class="token punctuation">.</span> balance</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Balance: 50.0</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Withdraw 100"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Withdraw 100</span> customer <span class="token punctuation">.</span> <span class="token function">withdraw</span> <span class="token punctuation">(</span> amount <span class="token punctuation">:</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token comment">// Insufficient funds.</span> <span class="token function">print</span> <span class="token punctuation">(</span> <span class="token string-literal"><span class="token string">"Balance: </span><span class="token interpolation-punctuation punctuation">(</span> <span class="token interpolation">customer <span class="token punctuation">.</span> account <span class="token punctuation">.</span> balance</span> <span class="token interpolation-punctuation punctuation">)</span> <span class="token string">"</span></span> <span class="token punctuation">)</span> <span class="token comment">// Balance: 50.0</span> |
=> At the last withdrawal, the balance in my account was only 50 dong. However, I made a withdrawal of 100 dong. So the system will print out the message: ” Insufficient funds “, and restore the balance to 50 VND for me
=> Right for our purpose. Great, isn’t it?
So we’ve gone through an example of using the Memento Pattern. We will come to the definition / theory of Memento Pattern
3. What is the Memento Pattern?
- The Memento Pattern belongs to the Behavioral Design Pattern group. Because this pattern is about the action of saving and restoring data. It will help us to save and restore the previous state of an object without revealing how we saved or retrieved it.
- Memento is a behavioral design pattern that lets you save and restore the previous state of an object without revealing the details of its implementation. – Dive Into Design Pattern
Those are some definitions of the Memento Pattern. We have the following diagram:
- Memento Pattern it will include 3 parts:
- Originator : A class that can save (we call it a snapshot) its state, or restore (restore) the state if needed. In example item 2,
BankAccount
is our Originator . - Memento : Is the place to store the properties that we need to save. We should make this Memento class immutable (cannot be changed) and only pass data once through the constructor (init). In the example item 2, our
Memento
needs to save the balance - Care Taker : A place to save and restore Memento from Originator . Care Taker will know when and why (when and why) to save the original value and when to restore the original value.
- Originator : A class that can save (we call it a snapshot) its state, or restore (restore) the state if needed. In example item 2,
4. When to use the Memento Pattern? ️
- As mentioned above, we use the Memento Pattern to save and restore the state of a certain object/class. In addition, it also helps us not to violate the principle of encapsulation (Encapsulation).
- Use the momento pattern whenever you want to save and later restore an object’s state. – Design Patterns by Tutorials
- Use the memento pattern when you want to produce snap-shots of the object’s state to be able to restore a previous state of the object – Dive Into Design Pattern
- Use the pattern when direct access to the object’s fields/get- ters/setters violates its encapsulation. – Dive Into Design Pattern
5. Pros and cons
5.1. Advantage
- Help us to save and restore the previous state of the object/class
- Help us not to violate the principle of encapsulation (Encapsulation)
5.2. Defect
- The application can use a lot of memory because users can create continuous Memento (for example, we save too many snapshots).
- “Caretakers should track the originator’s lifecycle to be able to destroy obsolete mementos.” – Dive Into Design Pattern
6. Conclusion
- So we have learned what the Memento Pattern is and how to install it through this blog.
- If you have questions or have a way to optimize your Memento Pattern example, don’t hesitate to comment. Thank you so much = Thank a lot = Thank you very much.
Some good examples you can refer to:
- https://github.com/ochococo/Design-Patterns-In-Swift#-memento
- https://code.tutsplus.com/courses/swift-design-patterns/lessons/memento