Skip to content

Commit 9bcce1a

Browse files
committed
Finished the implementation for Memento pattern
1 parent 6f86ead commit 9bcce1a

File tree

1 file changed

+14
-13
lines changed
  • Common Design Patterns/Behavioral/Memento

1 file changed

+14
-13
lines changed

Common Design Patterns/Behavioral/Memento/Memento.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Memento Design Pattern
2-
`Memento` is a behavioral design pattern that allows to save and restore an object's state without breaking encapsulation. Our implementation will be slightly differnet than the classic one, which splits the pattern into three components: `memento`, `carataker` and `originator`. `Caretaker` will be responsible for saving and restoring a `memento` object using a concrete implementation of storage and `memento` will be represented as a protocol that defines a contract for all types that should have storing/restoring capabiliy.
2+
`Memento` is a behavioral design pattern that allows to save and restore an object's state without breaking encapsulation. Our implementation will be slightly different than the classic one, which splits the pattern into three components: `memento`, `caretaker` and `originator`. `Caretaker` will be responsible for saving and restoring a `memento` object using a concrete implementation of storage and `memento` will be represented as a protocol that defines a contract for all types that should have saving/restoring capabilities.
33

44
## Implementation
55
We start off from declaring `Memento` protocol:
@@ -18,7 +18,7 @@ protocol Memento {
1818
```
1919
The protocol defines a read-only property for dictionary can be store `Any` type for a `String` key. The dictionary will be used to store and restore the internal state of a particular type. Then we defined a required, failable initializer that accepts a dictionary as an input parameter. The initializer will be used by the `Caretaker` instance to restore an object.
2020

21-
Next, we implement a protocol called `Caretaker`. The protocol will be resposible for declaring contract for all kinds of concrete mechanisms that will actually save and restore *memento* objects.
21+
Next, we implement a protocol called `Caretaker`. The protocol will be responsible for declaring contract for all kinds of concrete mechanisms that will actually save and restore *memento* objects.
2222

2323
```swift
2424
protocol Caretaker {
@@ -35,13 +35,13 @@ protocol Caretaker {
3535
}
3636
```
3737

38-
The `states` property that is dicttionary of types `String` and `String` for keys and values, that will be internally holding states, in order to be able to re-use or reference them at run-time. As an `API` the protocol defines `three` methods for saving a `memento` for a given state, restoring from state and deleting the state of a `memento` object.
38+
The `states` property that is a dictionary of types `String` and `String` for keys and values, that will be internally holding states, in order to be able to re-use or reference them at run-time. As an `API` the protocol defines `three` methods for saving a `memento` for a given state, restoring from state and deleting the state of a `memento` object.
3939

40-
Let's implement a concrete `Caretaker` called `PropertyListCarataker` that is built around `UserDefaults` mechanism:
40+
Let's implement a concrete `Caretaker` called `PropertyListCaretaker` that is built around `UserDefaults` mechanism:
4141

4242
```swift
4343
@dynamicMemberLookup
44-
struct PropertyListCarataker: Caretaker {
44+
struct PropertyListCaretaker: Caretaker {
4545

4646
// MARK: - Private properties
4747

@@ -51,7 +51,7 @@ struct PropertyListCarataker: Caretaker {
5151
// MARK: - Initializers
5252

5353
init() {
54-
if let states = standardDefaults.object(forKey: PropertyListCarataker.STATES_KEY) as? [String : String] {
54+
if let states = standardDefaults.object(forKey: PropertyListCaretaker.STATES_KEY) as? [String : String] {
5555
print("restored state keys: ", states)
5656
self.states = states
5757
}
@@ -61,7 +61,7 @@ struct PropertyListCarataker: Caretaker {
6161

6262
var states: [String : String] = [:] {
6363
didSet {
64-
standardDefaults.set(states, forKey: PropertyListCarataker.STATES_KEY)
64+
standardDefaults.set(states, forKey: PropertyListCaretaker.STATES_KEY)
6565
}
6666
}
6767

@@ -91,7 +91,7 @@ struct PropertyListCarataker: Caretaker {
9191
}
9292
```
9393

94-
The implementation is based on `struct`. We defiend a private `UserDefaults` property for convenient re-use and added conformance for `Carataker` protocol. The implementation is pretty straightforward, we used the built-in `API` for persistance. The interesting thing here is a relatively new feature of `Swift` called `dynamic member lookup`. We will use it to syntatically make the code more clear, however you may skip it, since it's not mandatory.
94+
The implementation is based on `struct`. We defined a private `UserDefaults` property for convenient re-use and added conformance for `Caretaker` protocol. The implementation is pretty straightforward, we used the built-in `API` for persistence. The interesting thing here is a relatively new feature of `Swift` called `dynamic member lookup`. We will use it to syntactically make the code more clear, however you may skip it, since it's not mandatory.
9595

9696
The final part of our implementation is to create a couple of classes that will conform to `Memento` protocol. Let's implement a `User` and an `Animal` classes:
9797

@@ -145,11 +145,11 @@ class Animal: Memento {
145145
}
146146
}
147147
```
148-
The approach that is used to save and restore the state using the dictionary is pretty similar to the one used with `NSCoding` protocol: we save the properes for the specified keys and then in the required, failable initializer we restore them. Firther, it can be improved with more type-safe keys, rather than using `String` literals.
148+
The approach that is used to save and restore the state using the dictionary is pretty similar to the one used with `NSCoding` protocol: we save the properties for the specified keys and then in the required, failable initializer we restore them. Futher it can be improved with more type-safe keys, rather than using `String` literals.
149149

150150
## Usage
151151

152-
The usage of the `Memento` pattern is all about creating objects, saving states by using a concrete `Carataker` and later on restoring a state by using one of the `String` keys.
152+
The usage of the `Memento` pattern is all about creating objects, saving states by using a concrete `Caretaker` and later on restoring a state by using one of the `String` keys.
153153

154154
```swift
155155
// A new user - John
@@ -161,7 +161,7 @@ var animal = Animal(name: "Monkey", age: 8)
161161
// name: John, age: 26, address: New Ave, 456
162162
// name: Monkey, age: 8
163163

164-
var caretaker = PropertyListCarataker()
164+
var caretaker = PropertyListCaretaker()
165165

166166
// We save the default states of the user and animal
167167
caretaker.save(memento: user, for: "defaultUser")
@@ -184,7 +184,7 @@ animal.name = "Cat"
184184
// name: Cat, age: 10
185185
```
186186

187-
In the presented snippet of code, we created a user and an animal. Then we created a `PropertyListCarataker` instnace and saved the states of the `memento` objects. Then we changed the ages of the objects and again saved then by using different state-keys. Finally, we changed the names of the `memento` objects to be able to verify that we actually are able to save and restore objects using varios state-keys.
187+
In the presented snippet of code, we created a user and an animal. Then we created a `PropertyListCaretaker` instance and saved the states of the `memento` objects. Then we changed the ages of the objects and again saved then by using different state-keys. Finally, we changed the names of the `memento` objects to be able to verify that we actually are able to save and restore objects using various state-keys.
188188

189189
```swift
190190
if let restoredUser = caretaker.defaultUser as User? {
@@ -205,4 +205,5 @@ if let restoredAnimal = caretaker.defaultAnimal02 as Animal? {
205205
```
206206
The first saved state for the state-key named `defaultUser` is correct! As well as the remaining states. We are able to restart our `macOS` or `iOS` application and be able to restore any object that conforms to the `Memento` protocol to a certain state.
207207

208-
## Conclusion
208+
## Conclusion
209+
`Memento` pattern is quite useful in many cases: when implementing undo manager, also to save objects when a user closes an app, so they can be restored when the app is launched again. These are just a few examples, when there are many more practical applications. The presented pattern's implementation can be further improved in order to be used in production code. For example the part that is responsible for *caretaking* of the `memento` objects, can be implemented using more reliable persistence approach. For example, in addition to `UserDefaults` `caretaker` you can implement `CoreData`-based `caretaker` that is more reliable and allows much more than just storing type's properties.

0 commit comments

Comments
 (0)