Đôi khi chúng ta cần phải ghi lại trạng thái bên trong của một đối tượng. Điều này là bắt buộc khi thực hiện tại các điểm kiểm tra và cung cấp cơ chế hoàn tác cho phép người dùng có thể khôi phục từ các lỗi. Chúng ta phải lưu thông tin trạng thái ở đâu đó để có thể khôi phục các đối tượng về trạng thái trước đó của chúng. Nhưng các đối tượng thường đóng gói một phần hoặc tất cả trạng thái của chúng, khiến nó không thể truy cập được vào các đối tượng khác và không thể lưu ở bên ngoài. Public các trạng thái này sẽ vi phạm nguyên tắc đóng gói, có thể làm giảm độ tin cậy và khả năng mở rộng của ứng dụng. Trong những trường hợp như vậy chúng ta có thể nghĩ đến Memento Pattern, nó sẽ giúp chúng ta giải quyết vấn đề này.
What is Memento Pattern ?
Memento là một trong những Pattern thuộc nhóm hành vi (Behavior Pattern). Memento là mẫu thiết kế có thể lưu lại trạng thái của một đối tượng để khôi phục lại sau này mà không vi phạm nguyên tắc đóng gói.
Dữ liệu trạng thái đã lưu trong đối tượng memento không thể truy cập bên ngoài đối tượng được lưu và khôi phục. Điều này bảo vệ tính toàn vẹn của dữ liệu trạng thái đã lưu.
Hoàn tác (Undo) hoặc ctrl + z là một trong những thao tác được sử dụng nhiều nhất trong trình soạn thảo văn bản (editor). Mẫu thiết kế Memento được sử dụng để thực hiện thao tác Undo. Điều này được thực hiện bằng cách lưu trạng thái hiện tại của đối tượng mỗi khi nó thay đổi trạng thái, từ đó chúng ta có thể khôi phục nó trong mọi trường hợp có lỗi.
How to setup Memento Pattern?
Các thành phần tham gia mẫu Memento:
Originator : đại diện cho đối tượng mà chúng ta muốn lưu. Nó sử dụng memento để lưu và khôi phục trạng thái bên trong của nó.
Caretaker : Nó không bao giờ thực hiện các thao tác trên nội dung của memento và thậm chí nó không kiểm tra nội dung. Nó giữ đối tượng memento và chịu trách nhiệm bảo vệ an toàn cho các đối tượng. Để khôi phục trạng thái trước đó, nó trả về đối tượng memento cho Originator.
Memento : đại diện cho một đối tượng để lưu trữ trạng thái của Originator. Nó bảo vệ chống lại sự truy cập của các đối tượng khác ngoài Originator.
Lớp Memento cung cấp 2 interfaces: 1 interface cho Caretaker và 1 cho Originator. Interface Caretaker không được cho phép bất kỳ hoạt động hoặc bất kỳ quyền truy cập vào trạng thái nội bộ được lưu trữ bởi memento và do đó đảm bảo nguyên tắc đóng gói. Interface Originator cho phép nó truy cập bất kỳ biến trạng thái nào cần thiết để có thể khôi phục trạng thái trước đó.
Lớp Memento thường là một lớp bên trong của Originator. Vì vậy, originator có quyền truy cập vào các trường của memento, nhưng các lớp bên ngoài không có quyền truy cập vào các trường này.
Benefit of Memento Pattern
- Lợi ích:
Bảo đảm nguyên tắc đóng gói: sử dụng trực tiếp trạng thái của đối tượng có thể làm lộ thông tin chi tiết bên trong đối tượng và vi phạm nguyên tắc đóng gói.
Đơn giản code của Originator bằng cách để Memento lưu giữ trạng thái của Originator và Caretaker quản lý lịch sử thay đổi của Originator.
- Một số vấn đề cần xem xét khi sử dụng Memento Pattern:
Khi có một số lượng lớn Memento được tạo ra có thể gặp vấn đề về bộ nhớ, performance của ứng dụng.
Khó đảm bảo trạng thái bên trong của Memento không bị thay đổi.
How to USE Memento Patter
- Các ứng dụng cần chức năng cần Undo/ Redo: lưu trạng thái của một đối tượng bên ngoài và có thể restore/ rollback sau này.
- Thích hợp với các ứng dụng cần quản lý transaction.
iOS Case Study
ở đây chúng ta sẽ thử tìm hiểu và thay thế các method theo Memento Pattern trong IOS, các phương thức persist() và recover() được thêm vào Memento protocol nó cung cấp cho developer nơi để lưu trữ và huỷ lưu trữ các thuộc tính.
Mỗi tên thuộc tính tương ứng với một khóa phần tử từ điển và mỗi giá trị thuộc tính tương ứng với giá trị phần tử từ điển phù hợp với khóa.
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | <span class="token keyword">import</span> <span class="token builtin">Foundation</span> <span class="token keyword">protocol</span> <span class="token builtin">Memento</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> stateName<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> state<span class="token punctuation">:</span> <span class="token builtin">Dictionary</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">String</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token keyword">set</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">restore</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">persist</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">recover</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Memento</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token builtin">UserDefaults</span><span class="token punctuation">.</span>standard<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>state<span class="token punctuation">,</span> forKey<span class="token punctuation">:</span> stateName<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">restore</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token keyword">let</span> dictionary <span class="token operator">=</span> <span class="token builtin">UserDefaults</span><span class="token punctuation">.</span>standard<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span>forKey<span class="token punctuation">:</span> stateName<span class="token punctuation">)</span> <span class="token keyword">as</span><span class="token operator">!</span> <span class="token builtin">Dictionary</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">String</span><span class="token operator">></span><span class="token operator">?</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> dictionary <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> state<span class="token punctuation">.</span><span class="token function">removeAll</span><span class="token punctuation">(</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">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> line <span class="token operator">=</span> <span class="token string">""</span> <span class="token keyword">if</span> state<span class="token punctuation">.</span><span class="token builtin">count</span> <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token keyword">in</span> state <span class="token punctuation">{</span> line <span class="token operator">+</span><span class="token operator">=</span> key <span class="token operator">+</span> <span class="token string">": "</span> <span class="token operator">+</span> value <span class="token operator">+</span> <span class="token string">"n"</span> <span class="token punctuation">}</span> <span class="token function">print</span><span class="token punctuation">(</span>line<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Empty entity.n"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">User</span><span class="token punctuation">:</span> <span class="token builtin">Memento</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> stateName<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">var</span> state<span class="token punctuation">:</span> <span class="token builtin">Dictionary</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">String</span><span class="token operator">></span> <span class="token keyword">var</span> firstName<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">var</span> lastName<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">var</span> age<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token keyword">init</span><span class="token punctuation">(</span>firstName<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">,</span> lastName<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">,</span> stateName<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>firstName <span class="token operator">=</span> firstName <span class="token keyword">self</span><span class="token punctuation">.</span>lastName <span class="token operator">=</span> lastName <span class="token keyword">self</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age <span class="token keyword">self</span><span class="token punctuation">.</span>stateName <span class="token operator">=</span> stateName <span class="token keyword">self</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token builtin">Dictionary</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">String</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">persist</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">init</span><span class="token punctuation">(</span>stateName<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>stateName <span class="token operator">=</span> stateName <span class="token keyword">self</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token builtin">Dictionary</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">String</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">self</span><span class="token punctuation">.</span>firstName <span class="token operator">=</span> <span class="token string">""</span> <span class="token keyword">self</span><span class="token punctuation">.</span>lastName <span class="token operator">=</span> <span class="token string">""</span> <span class="token keyword">self</span><span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token string">""</span> <span class="token function">recover</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">persist</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> state<span class="token punctuation">[</span><span class="token string">"firstName"</span><span class="token punctuation">]</span> <span class="token operator">=</span> firstName state<span class="token punctuation">[</span><span class="token string">"lastName"</span><span class="token punctuation">]</span> <span class="token operator">=</span> lastName state<span class="token punctuation">[</span><span class="token string">"age"</span><span class="token punctuation">]</span> <span class="token operator">=</span> age <span class="token function">save</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">recover</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">restore</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> state<span class="token punctuation">.</span><span class="token builtin">count</span> <span class="token operator">></span> <span class="token number">0</span> <span class="token punctuation">{</span> firstName <span class="token operator">=</span> state<span class="token punctuation">[</span><span class="token string">"firstName"</span><span class="token punctuation">]</span><span class="token operator">!</span> lastName <span class="token operator">=</span> state<span class="token punctuation">[</span><span class="token string">"lastName"</span><span class="token punctuation">]</span><span class="token operator">!</span> age <span class="token operator">=</span> state<span class="token punctuation">[</span><span class="token string">"age"</span><span class="token punctuation">]</span><span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span>firstName <span class="token operator">=</span> <span class="token string">""</span> <span class="token keyword">self</span><span class="token punctuation">.</span>lastName <span class="token operator">=</span> <span class="token string">""</span> <span class="token keyword">self</span><span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token string">""</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Code implementing trong class ViewController.swift:
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 | <span class="token keyword">import</span> <span class="token builtin">UIKit</span> <span class="token keyword">class</span> <span class="token class-name">ViewController</span><span class="token punctuation">:</span> <span class="token builtin">UIViewController</span> <span class="token punctuation">{</span> <span class="token atrule">@IBOutlet</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> firstNameTextField<span class="token punctuation">:</span> <span class="token builtin">UITextField</span><span class="token operator">!</span> <span class="token atrule">@IBOutlet</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> lastNameTextField<span class="token punctuation">:</span> <span class="token builtin">UITextField</span><span class="token operator">!</span> <span class="token atrule">@IBOutlet</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> ageTextField<span class="token punctuation">:</span> <span class="token builtin">UITextField</span><span class="token operator">!</span> <span class="token keyword">override</span> <span class="token keyword">func</span> <span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">viewDidLoad</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token atrule">@IBAction</span> <span class="token keyword">func</span> <span class="token function">saveUserTapped</span><span class="token punctuation">(</span><span class="token number">_</span> sender<span class="token punctuation">:</span> <span class="token builtin">Any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> firstNameTextField<span class="token punctuation">.</span>text <span class="token operator">!=</span> <span class="token string">""</span> <span class="token operator">&&</span> lastNameTextField<span class="token punctuation">.</span>text <span class="token operator">!=</span> <span class="token string">""</span> <span class="token operator">&&</span> ageTextField<span class="token punctuation">.</span>text <span class="token operator">!=</span> <span class="token string">""</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> user <span class="token operator">=</span> <span class="token function">User</span><span class="token punctuation">(</span>firstName<span class="token punctuation">:</span> firstNameTextField<span class="token punctuation">.</span>text<span class="token operator">!</span><span class="token punctuation">,</span> lastName<span class="token punctuation">:</span> lastNameTextField<span class="token punctuation">.</span>text<span class="token operator">!</span><span class="token punctuation">,</span> age<span class="token punctuation">:</span> ageTextField<span class="token punctuation">.</span>text<span class="token operator">!</span><span class="token punctuation">,</span> stateName<span class="token punctuation">:</span> <span class="token string">"userKey"</span><span class="token punctuation">)</span> user<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token atrule">@IBAction</span> <span class="token keyword">func</span> <span class="token function">restoreUserTapped</span><span class="token punctuation">(</span><span class="token number">_</span> sender<span class="token punctuation">:</span> <span class="token builtin">Any</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> user <span class="token operator">=</span> <span class="token function">User</span><span class="token punctuation">(</span>stateName<span class="token punctuation">:</span> <span class="token string">"userKey"</span><span class="token punctuation">)</span> firstNameTextField<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>firstName lastNameTextField<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>lastName ageTextField<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>age user<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |