You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Common Design Patterns/Behavioral/Memento/Memento.md
+14-13Lines changed: 14 additions & 13 deletions
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
# 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.
3
3
4
4
## Implementation
5
5
We start off from declaring `Memento` protocol:
@@ -18,7 +18,7 @@ protocol Memento {
18
18
```
19
19
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.
20
20
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.
22
22
23
23
```swift
24
24
protocolCaretaker {
@@ -35,13 +35,13 @@ protocol Caretaker {
35
35
}
36
36
```
37
37
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.
39
39
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:
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.
95
95
96
96
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:
97
97
@@ -145,11 +145,11 @@ class Animal: Memento {
145
145
}
146
146
}
147
147
```
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.
149
149
150
150
## Usage
151
151
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.
// We save the default states of the user and animal
167
167
caretaker.save(memento: user, for: "defaultUser")
@@ -184,7 +184,7 @@ animal.name = "Cat"
184
184
// name: Cat, age: 10
185
185
```
186
186
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.
188
188
189
189
```swift
190
190
iflet restoredUser = caretaker.defaultUser as User? {
@@ -205,4 +205,5 @@ if let restoredAnimal = caretaker.defaultAnimal02 as Animal? {
205
205
```
206
206
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.
207
207
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