메멘토?
단어 자체가 익숙하지 않습니다.
메멘토(Memento)는 기억의 증표를 말한다 - 위키백과 -
비슷한 단어로 “메멘토 모리” 라는 단어가 있습니다.
그 단어의 뜻은 “너는 반드시 죽는다는 것을 기억해라.” 라는 뜻이라고 합니다.
메멘토는 무언가 “기억” 하는 행위와 관련된 의미를 가지고 있네요.
메멘토 패턴은 행동패턴 (Behavioral Patterns) 에 속합니다.
왜냐하면 “데이터를 저장하고 복원하는 행동” 에 초첨을 맞췄기 때문이죠.
먼저 클래스 다이어그램을 보겠습니다.
메멘토 패턴은 3 개의 객체로 구성됩니다.
Use the memento pattern whenever you want to save and later restore an object’s state.
(메멘토 패턴은 특정 상태를 저장하고 싶을 때, 언제든 사용하면 됩니다.)
좀 더 와닿는 예시를 들면, “문서작업” 이나 “드로잉작업(컴퓨터로)” 이 있습니다.
문서작업이나 그림을 그리다보면, 가장많이 사용하는 기능인 “실행 취소 (컨트롤 + Z)” 아시죠.
이 Task가 동작하려면 이전 상태에 대한 정보를 가지고 있어야 가능하겠죠.
이런 상황에서 “메멘토 패턴” 을 사용합니다.
메멘토 패턴은 객체의 상태정보를 저장하고, 저장한 정보를 통해서 복원할 수 있도록 하는 패턴입니다.
비슷한 다른 예시로 게임이 있습니다.
오리지네티어는 게임 상태를 저장할 겁니다.
ex. 현재 레벨, HP, 죽은 횟수 등등
메멘토는 저장된 데이터 정보를 가지고 있겠죠.
케어테이커는 게임 시스템이되겠습니다.
코드를 보겠습니다.
import Foundation
// MARK: - Originator
public class Game: Codable {
public class State: Codable {
public var attemptsRemaining: Int = 3
public var level: Int = 1
public var score: Int = 0
}
public var state = State()
public func rackUpMassivePoints() {
state.score += 9002
}
public func monstersEatPlayer() {
state.attemptsRemaining -= 1
}
}
“Game” 클래스가 있습니다. 그리고 그 내부에 다시 “State” 클래스가 있죠. State 클래스의 멤버변수로 “attemptsRemaining”, “level” 그리고 “score” 가 있습니다.
다시 Game 클래스에 멤버변수와 메소드로 “state”, “rackUpMassivePoints” 그리고 “monstersEatPlayer” 가 있습니다.
메소드를 보면 멤버변수인 “state”에 연산한 이후 값을 할당해주고 있네요.
그런데 각 클래스에보면 Codable
이라는 친구를 채택하고 있습니다.
Codable은 Swift4 에 도입된 개념입니다.
Codable을 준수한다면, 어떤 타입이든지
(애플의 말을 옮기면)
“convert itself into and out of an external representation.”
해석해보면, “자기자신을 변환하거나 외부 표현으로 변환한다.” 정도로 해석되네요.
이게 무슨말이냐..
Codable을 채택한 객체는 “변환”이 가능하다는 겁니다. 그런게 그것들이 JSON 과 같은 외부표현으로도 가능하다는 겁니다.
Codable에는 두 가지 프로토콜이 또 있습니다.
본론으로 돌아와서 Memento를 선언하겠습니다.
typealias GameMemento = Data
위 코드는 사용자 타입정의입니다. 그러므로 꼭 필요한 코드는 아닙니다.
다만, “GameMemento” 가 Data 타입이라는 것에 주목하시면 됩니다.
Encoder를 통해서 데이터를 저장할 것이고 Decoder를 통해서 데이터를 복원할 예정입니다. (Codable에 대해 잠깐 설명한 이유입니다.)
하나 남았죠?
CareTaker를 작성하겠습니다.
public class GameSystem {
// 1 코드화 인스턴스 생성 및 저장소 생성
private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
private let userDefaults = UserDefaults.standard
// 2 게임데이터를 저장하는 메소드
public func save(_ game: Game, title: String) throws {
let data = try encoder.encode(game)
userDefault.set(data, forKey: title)
}
// 3 게임데이터를 불러오는 메소드
public func load(title: String) throws -> Game {
guard let data = userDefaults.data(forKey: title),
let game = try? decoder.decode(Game.self, from: data)
else { throw Error.gameNotFound }
return game
}
// 에러 정의
public enum Error: String, Error {
case gameNotFound
}
}
이제 실행해보겠습니다.
// MARK: - Example
var game = Game()
game.monstersEatPlayer()
game.rackUpMassivePoints()
게임을 하다가 저장을 해야한다면, 다음과 같이 호출할겁니다.
// Save Game
let gameSystem = GameSystem()
try gameSystem.save(game, title: "Best Game Ever")
새로운 게임이 하고싶으면 새로운 인스턴스를 생성할겁니다.
// New Game
game = Game()
print("New Game Score: \(game.state.score)")
// New Game Score: 0
이전 게임 목록을 load 하고 싶다면,
// Load Game
game = try! gameSystem.load(title: "Best Game Ever")
print("Loaded Game Score: \(game.state.score)")
// Loaded Game Score: 9002
메멘토 패턴 예제 · GitHub
해당 예제는 아래 블로그를 참고해서 작성했습니다.
Swift 디자인 패턴 Memento Pattern (메멘토) - 디자인 패턴 공부 19
Q. 메멘토 패턴은 무엇인가?
Q. 메멘토 패턴은 언제 사용하는가?
Q. 메멘토 패턴의 구성요소는 각각 어떤 역할을 하는가?
에 대한 대답을 할 수 있도록 글을 작성했습니다.
읽어주셔서 감사합니다.!