- vừa được xem lúc

Memento Pattern trong Swift iOS

0 0 31

Người đăng: Kiên HT

Theo Viblo Asia

Độ khó: Beginner | Easy | Normal | Challenging
Xcode 14.0.1
Swift 5.7


1. Giới thiệu 👋

Trong bài viết này mình sẽ giới thiệu đến các bạn một Fundamental Design Pattern - Memento Pattern. Vậy Memento Pattern là một pattern như thế nào, ứng dụng và cách cài đặt nó như thế nào?


Như thường lệ, mình sẽ không nói về lý thuyết/định nghĩa ngay. Mình sẽ trình bày một ví dụ về Memento Pattern trước, sau đó chúng ta sẽ đi sâu vào xem bản chất của nó nhé !!!

2. Đặt vấn đề 🤔

Mình có một đoạn code như sau:

class BankAccount { private(set) var balance: Double = 0.0 func setBalance(balance: Double) { self.balance = balance }
} class BankCustomer { let account: BankAccount init(account: BankAccount) { self.account = account } func withdraw(amount: Double) { account.setBalance(balance: account.balance - amount) } func deposit(amount: Double) { account.setBalance(balance: account.balance + amount) }
}
  • Trong đoạn code trên, mình có 2 class là BankCustomerBankAccount. Mỗi khách hàng sẽ có một tài khoản ngân hàng và số dư riêng. Dễ hiểu chứ nhỉ?
  • Khi chúng ta rút tiền thì hàm withdraw(amount:) sẽ được gọi đến.
  • Mọi thứ trông có vẻ ổn? Vậy vấn đề ở đây là gì nhỉ?
  • Giả sử mình muốn chuyển tiền đến cho người thân, sau khi hệ thống gọi đến hàm withdraw(amount:) và số dư của mình đã bị trừ đi. Tuy nhiên mình đã nhập sai số tài khoản của người thân, từ đây hệ thống phải trả lại số tiền mình đã gửi cho mình đúng không nhỉ? Vậy là code phía trên chưa làm được việc restore lại số dư cho mình rồi
  • Hoặc giả sử mình chỉ có 50 đồng trong tài khoản nhưng mình muốn rút đến 100 đồng. Sau khi hệ thống trừ 100 đồng thì số dư của mình không đủ. Vậy giao dịch rút 100 đồng đó là không hợp lệ, nên hệ thống cần phải restore lại số tiền là 50 đồng cho tài khoản của mình.

=> Đó cũng là lý do chúng ta có Memento Pattern để giúp restore lại trạng thái trước đó của một object và vẫn không vi phạm nguyên lý đóng gói (Encapsulation)
=> Cùng mình áp dụng Memento Pattern vào example code phía trên nhé

3. Ví dụ về Memento Pattern 😎

Sau đây là ví dụ về Memento Pattern của 2 class BankCustomerBankAccount :

protocol Originator { func save() -> Memento func restore(memento: Memento)
} class BankAccount: Originator { private(set) var balance: Double = 0.0 func setBalance(balance: Double) { self.balance = balance } func save() -> Memento { return Memento(balance: balance) } func restore(memento: Memento) { balance = memento.balance }
} class Memento { let balance: Double init(balance: Double) { self.balance = balance }
} class BankCustomer { let account: BankAccount init(account: BankAccount) { self.account = account } func withdraw(amount: Double) { let memento = account.save() account.setBalance(balance: account.balance - amount) if account.balance < 0 { account.restore(memento: memento) print("Insufficient funds.") } } func deposit(amount: Double) { account.setBalance(balance: account.balance + amount) }
}
  • Có thể hơi khó hiểu phải không mọi người 😁. Tuy nhiên đừng lo lắng, chúng ta sẽ tìm hiểu từng thành phần trong Memento Pattern ngay sau đây.
  • Trước đó hãy thử chạy phần code có sử dụng Memento Pattern trước nhé.
let account = BankAccount()
let customer = BankCustomer(account: account)
print("Balance: \(customer.account.balance)") // Balance: 0.0 print("Deposit 100") // Deposit 100
customer.deposit(amount: 100)
print("Balance: \(customer.account.balance)") // Balance: 100.0 print("Withdraw 50") // Withdraw 50
customer.withdraw(amount: 50)
print("Balance: \(customer.account.balance)") // Balance: 50.0 print("Withdraw 100") // Withdraw 100
customer.withdraw(amount: 100) // Insufficient funds.
print("Balance: \(customer.account.balance)") // Balance: 50.0

=> Ở lần withdraw cuối cùng, số dư (balance) trong tài khoản của mình chỉ còn 50 đồng. Tuy nhiên mình lại thực hiện lệnh rút 100 đồng. Nên hệ thống sẽ in ra thông báo: "Insufficient funds", và restore lại số dư là 50 đồng cho mình
=> Đã đúng mục đích của chúng ta nhỉ. Tuyệt vời, phải không?


Vậy là chúng ta đã xem qua một ví dụ về việc sử dụng Memento Pattern rồi. Chúng ta sẽ đến với phần định nghĩa / lý thuyết của Memento Pattern nhé

3. Memento Pattern là gì? 🤔

  • Memento Pattern thuộc nhóm Behavioral Design Pattern. Bởi vì pattern này là pattern nói về hành động lưu và khôi phục dữ liệu. Nó sẽ giúp chúng ta lưu và khôi phục trạng thái trước đó của một object mà không làm tiết lộ cách mà chúng ta lưu hoặc lấy lại.
  • 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

Đó là một số định nghĩa của Memento Pattern. Chúng ta có sơ đồ như sau:

  • Memento Pattern nó sẽ gồm 3 phần:
    • Originator: Là một class mà chúng có thể lưu (chúng ta hay gọi là snapshot) lại trạng thái của nó, hoặc khôi phục (restore) trạng thái nếu cần. Ở ví dụ mục 2, BankAccountOriginator của chúng ta.
    • Memento: Là nơi chứa những property mà chúng ta cần lưu lại. Chúng ta nên cho class Memento này immutable (không thể thay đổi) và chỉ truyền data 1 lần duy nhất thông qua hàm khởi tạo (init). Ở ví dụ mục 2, Memento của chúng ta cần lưu lại balance
    • Care Taker: Là nơi lưu và khôi phục Memento từ Originator. Care Taker sẽ biết được lúc nào và tại sao (when and why) cần lưu lại giá trị gốc và khi nào thì cần được khôi phục giá trị gốc.

4. Khi nào thì sử dụng Memento Pattern? ⏳️

  • Như đã nói ở trên, chúng ta sử dụng Memento Pattern để lưu và khôi phục trạng thái của một object/class nào đó. Ngoài ra nó còn giúp chúng ta không vi phạm nguyên tắc bao đóng (Encapsulation)
  • Use the memento pattern whenever you want to save and later restore an object’s state. - Design Pattern 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. Ưu và nhược điểm 🤙

5.1. Ưu điểm

  • Giúp chúng ta lưu và khôi phục trạng thái trước đó của object/class
  • Giúp chúng ta không bị vi phạm nguyên tắc bao đóng (Encapsulation)

5.2. Nhược điểm

  • Ứng dụng có thể sử dụng rất nhiều bộ nhớ bởi vì người dùng có thể tạo Memento liên tục (ví dụ chúng ta lưu lại snapshot quá nhiều chẳng hạn)
  • "Caretakers should track the originator’s lifecycle to be able to destroy obsolete mementos." - Dive Into Design Pattern

6. Kết luận 📔

  • Vậy là chúng ta đã tìm hiểu xong Memento Pattern là gì và cách cài đặt nó như thế nào thông qua blog này.
  • Nếu các bạn có thắc mắc hay có cách nào tối ưu được ví dụ Memento Pattern của mình thì đừng ngần ngại hãy comment nhé. Mình cảm ơn các bạn rất nhiều.

Một số ví dụ hay các bạn có thể tham khảo:

Reference 🥳

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 281

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 207

- vừa được xem lúc

Swift: Tạo custom phép toán tử (Operator) của riêng bạn!

Swift cho phép bạn tạo các toán tử có thể tùy chỉnh của riêng bạn. Điều này đặc biệt hữu ích khi bạn xử lý các loại dữ liệu của riêng mình. Operator Types in Swift. Có năm loại toán tử chính trong Swift.

0 0 56

- vừa được xem lúc

Chương 6 Protocol oriented programming.

Cuốn sách này là về lập trình hướng protocol. Khi Apple thông báo swift 2 ở WWDC 2015.

0 0 46

- vừa được xem lúc

Ví dụ về UIActivityViewController

Trên iOS, UIActivityViewController cung cấp giao diện thống nhất để người dùng chia sẻ và thực hiện các hành động trên văn bản, hình ảnh, URL và các mục khác trong ứng dụng. let string = "Hello, world!". let url = URL(string: "https://nshipster.com").

0 0 58

- vừa được xem lúc

Quản lý self và cancellable trong Combine.

. . Công việc quản lý bộ nhớ memory management thường trở nên phức tạp khi chúng ta thực hiện các tác vụ bất đồng bộ asynchronous vì chúng ta thường phải lưu giữ một số object nằm ngoài scope mà object được define trong khi vẫn phải đảm bảo được việc giải phóng object đó được thực hiện đúng quy trìn

0 0 41