Contents
Chào mừng bạn đến với Fx Studio. Chủ đề bài viết lần này là “Memento Pattern“. Đây là bài viết về lý thuyết, nhằm giúp bạn thêm hiểu biết & tổ chức code một cách khoa học hợp lý hơn. Và muốn biết Memento Pattern làm được gì, thì chúng ta tiếp tục tìm hiểu nhóe.
Mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Bài viết sử dụng Swift là ngôn ngữ demo và áp dụng mẫu thiết kế vào trong iOS Project. Do đó, bạn cần có một chút kiến thức cơ bản về ngôn ngữ & nền tảng. Bạn có thể tham khảo qua các bài viết sau:
Okay rồi, thì quất nhóe!
Memento Pattern là gì?
Khái niệm
Memento Pattern là một mẫu thiết kế hành vi (behavioral design pattern) trong lập trình hướng đối tượng, nó cung cấp khả năng khôi phục trạng thái của một đối tượng về một thời điểm trước đó mà không vi phạm nguyên tắc đóng gói của đối tượng.
Memento Pattern thường được sử dụng trong các trường hợp cần thực hiện và hoàn tác các thao tác. Ví dụ như: trong một trình soạn thảo văn bản, bạn có thể “undo” (hoàn tác) các thao tác chỉnh sửa gần nhất.
Thành phần
Mô hình này bao gồm ba loại đối tượng chính:
- Originator: Đối tượng cần lưu trữ trạng thái hiện tại.
- Memento: Đối tượng lưu trữ trạng thái đã lưu của Originator.
- Caretaker: Đối tượng kiểm soát khi và làm thế nào Originator tương tác với Memento, nhưng không thao tác trực tiếp với trạng thái đã lưu.
Ví dụ
Một ví dụ về việc sử dụng Memento Pattern trong iOS có thể là việc lưu trạng thái của một ứng dụng khi nó chuyển sang chế độ nền và sau đó khôi phục trạng thái đó khi ứng dụng trở lại chế độ tiền cảnh.
class Memento { let state: String init(state: String) { self.state = state } } class Originator { var state: String = "" func createMemento() -> Memento { return Memento(state: state) } func restore(memento: Memento) { state = memento.state } } class Caretaker { var mementos = [Memento]() var originator: Originator init(originator: Originator) { self.originator = originator } func backup() { mementos.append(originator.createMemento()) } func undo() { guard let memento = mementos.popLast() else { return } originator.restore(memento: memento) } } let originator = Originator() let caretaker = Caretaker(originator: originator) originator.state = "State1" caretaker.backup() originator.state = "State2" caretaker.backup() originator.state = "State3" caretaker.backup() caretaker.undo() print(originator.state) // Prints: "State2" caretaker.undo() print(originator.state) // Prints: "State1"
Trong ví dụ này,
Originator
lưu trữ trạng thái hiện tạiMemento
lưu trữ trạng thái đã lưu củaOriginator
Caretaker
quản lý việc lưu và khôi phục các trạng thái từMemento
So sánh với hàng đợi & mảng
Memento không giống như một hàng đợi hay một mảng. Nó là một mẫu thiết kế trong lập trình hướng đối tượng. Nó không phải là một cấu trúc dữ liệu như hàng đợi hay mảng.
Memento Pattern được sử dụng để lưu trữ trạng thái của một đối tượng (được gọi là Originator) vào một đối tượng khác (được gọi là Memento). Một đối tượng thứ ba (được gọi là Caretaker) sau đó có thể sử dụng Memento để khôi phục trạng thái của Originator.
Tuy nhiên, bạn có thể sử dụng một mảng hoặc một hàng đợi để lưu trữ nhiều Memento, cho phép bạn khôi phục nhiều trạng thái trước đó của Originator. Trong trường hợp này, mảng hoặc hàng đợi đóng vai trò như một danh sách lịch sử các trạng thái đã lưu.
Ưu & nhược
Mẫu thiết kế Memento có một số ưu điểm và nhược điểm như sau:
Ưu điểm:
- Khôi phục trạng thái: Memento Pattern cho phép bạn lưu lại và khôi phục trạng thái của một đối tượng mà không vi phạm nguyên tắc đóng gói của đối tượng.
- Simplifying Originator: Bằng cách giữ trạng thái bên ngoài đối tượng gốc, Memento Pattern giúp giảm bớt trách nhiệm của đối tượng gốc, giúp mã nguồn dễ đọc và dễ bảo dưỡng hơn.
- Chức năng “undo”: Memento Pattern thường được sử dụng để cung cấp chức năng “undo” trong các ứng dụng.
Nhược điểm:
- Tốn bộ nhớ: Memento Pattern có thể tốn kém về mặt bộ nhớ. Đặc biệt nếu bạn lưu lại nhiều trạng thái hoặc trạng thái của các đối tượng lớn.
- Overhead: Việc tạo và lưu trữ mementos có thể tạo ra một lượng công việc đáng kể. Đặc biệt nếu trạng thái của đối tượng phức tạp hoặc nếu bạn cần lưu trữ nhiều mementos.
- Cần cẩn thận với trạng thái đối tượng: Nếu đối tượng gốc được thay đổi trong quá trình tạo memento. Có thể dẫn đến trạng thái không nhất quán hoặc không hợp lệ khi khôi phục.
Sử dụng
Memento Pattern thường được sử dụng trong các trường hợp sau:
- Khi bạn muốn lưu lại và khôi phục trạng thái của một đối tượng
- Memento Pattern cho phép bạn lưu lại trạng thái hiện tại của một đối tượng
- Sau đó, khôi phục lại trạng thái đó mà không vi phạm nguyên tắc đóng gói của đối tượng.
class Originator { var state: String init(state: String) { self.state = state } func createMemento() -> Memento { return Memento(state: state) } func restore(memento: Memento) { state = memento.state } } class Memento { let state: String init(state: String) { self.state = state } } let originator = Originator(state: "State1") let memento = originator.createMemento() originator.state = "State2" originator.restore(memento: memento) print(originator.state) // Prints: "State1"
- Khi bạn muốn cung cấp chức năng “undo”:
- Pattern thường được sử dụng để cung cấp chức năng “undo” trong các ứng dụng.
- Ví dụ, trong một trình soạn thảo văn bản. Bạn có thể sử dụng Memento Pattern để lưu lại trạng thái của văn bản sau mỗi thao tác chỉnh sửa. Cho phép người dùng “undo” các thao tác chỉnh sửa gần nhất.
class Caretaker { private var mementos = [Memento]() private var originator: Originator init(originator: Originator) { self.originator = originator } func backup() { mementos.append(originator.createMemento()) } func undo() { guard let memento = mementos.popLast() else { return } originator.restore(memento: memento) } } let originator = Originator(state: "State1") let caretaker = Caretaker(originator: originator) originator.state = "State2" caretaker.backup() originator.state = "State3" caretaker.backup() caretaker.undo() print(originator.state) // Prints: "State2" caretaker.undo() print(originator.state) // Prints: "State1"
- Khi bạn muốn lưu lại lịch sử của một đối tượng
- Memento Pattern cũng có thể được sử dụng để lưu lại lịch sử của một đối tượng
- Cho phép bạn xem lại các trạng thái trước đó của đối tượng
class Caretaker { private var mementos = [Memento]() private var originator: Originator init(originator: Originator) { self.originator = originator } func backup() { mementos.append(originator.createMemento()) } func showHistory() { for (index, memento) in mementos.enumerated() { print("Memento \(index): \(memento.state)") } } } let originator = Originator(state: "State1") let caretaker = Caretaker(originator: originator) originator.state = "State2" caretaker.backup() originator.state = "State3" caretaker.backup() caretaker.showHistory() // Prints: "Memento 0: State2", "Memento 1: State3"
Tuy nhiên, cần lưu ý rằng việc sử dụng Memento Pattern có thể tốn kém về mặt bộ nhớ, đặc biệt nếu bạn lưu lại nhiều trạng thái hoặc trạng thái của các đối tượng lớn.
Mã hóa & giải mã
Memento Pattern có thể được sử dụng trong việc mã hóa và giải mã dữ liệu. Dưới đây là một ví dụ về việc sử dụng Memento Pattern để mã hóa và giải mã một đối tượng trong Swift:
import Foundation class Memento { let state: Data init(state: Data) { self.state = state } } class Originator { var state: Any init(state: Any) { self.state = state } func createMemento() -> Memento { let data = try! NSKeyedArchiver.archivedData(withRootObject: state, requiringSecureCoding: false) return Memento(state: data) } func restore(memento: Memento) { state = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(memento.state)! } } class Caretaker { var mementos = [Memento]() var originator: Originator init(originator: Originator) { self.originator = originator } func backup() { mementos.append(originator.createMemento()) } func undo() { guard let memento = mementos.popLast() else { return } originator.restore(memento: memento) } } let originator = Originator(state: ["Hello", "World"]) let caretaker = Caretaker(originator: originator) caretaker.backup() originator.state = ["Hello", "Swift"] caretaker.backup() caretaker.undo() print(originator.state) // Prints: ["Hello", "World"]
Trong ví dụ này,
Originator
lưu trữ trạng thái hiện tại dưới dạng một đối tượng bất kỳMemento
lưu trữ trạng thái đã lưu củaOriginator
dưới dạng dữ liệuCaretaker
quản lý việc lưu và khôi phục các trạng thái từMemento
NSKeyedArchiver
vàNSKeyedUnarchiver
được sử dụng để mã hóa và giải mã trạng thái củaOriginator
.
Lưu ý:
Trong iOS, bạn có thể sử dụng các lớp như
NSKeyedArchiver
vàNSKeyedUnarchiver
(hoặcCodable
protocol vớiJSONEncoder
vàJSONDecoder
cho các đối tượng có thể mã hóa) để mã hóa và giải mã trạng thái của Originator. Tuy nhiên, đây không phải là một phần cốt lõi của mẫu thiết kế Memento, mà chỉ là một cách thực hiện cụ thể trong iOS.
Tạm kết
- Giới thiệu và giải thích mẫu thiết kế Memento trong lập trình.
- Mẫu thiết kế này cho phép lưu lại và khôi phục trạng thái của một đối tượng mà không vi phạm nguyên tắc đóng gói của đối tượng.
- Thành phần của mẫu thiết kế Memento
- Các cách dùng cơ bản & áp dụng thực tế trong dự án iOS
Okay! Tới đây, mình xin kết thúc bài viết giới thiệu về Memento Pattern. Nếu có gì thắc mắc hay góp ý cho mình. Thì bạn có thể để lại bình luận hoặc gửi email theo trang Contact.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)