- Swift
- Split View
- IOS
- CollectionView
- github
- iTerm
- TOSS
- 애플사이다
- 스위프트
- DiffableDataSource
- Apple
- Human Interface Guidelines
- 애플
- lineBreakStrategy
- LanguageGuide
- 야곰아카데미
- iPad
- orthogonalScrollingBehavior
- Combine+UIKit
- 디자인패턴
- Accessibility
- Keychain
- 앱개발
- UIKit
- 전달인자 레이블
- UILabel
- HIG
- lineBreakMode
- WWDC
- GOF
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Today
- Total
애플사이다의 iOS 개발 일지
[디자인 패턴] Memento Pattern - 실행취소 기능이 필요할 때 본문
안녕하세요. 애플사이다입니다.
디자인 패턴 중 하나인 Memento Pattern (메멘토 패턴)을 알아보겠습니다.
Memento란?
단기 기억상실증을 앓는 주인공이 등장하는 <메멘토>라는 영화나
"Memento Mori" (죽음을 기억하라)라는 표현으로 접해본 단어다.
Memento는 "기억을 상기시켜주는 Reminder 역할을 하는 것"을 의미한다.
Memento Pattern이란?
메멘토 패턴은 객체를 변경 이전 상태로 복원할 수 있는 패턴이다.
기억 (백업해둔 데이터)을 바탕으로 과거로 돌아갈 수 있다는 점을 살려 네이밍한 것 같다.
Originator, Memento, CareTaker 3개 요소로 구성된다.
행동 패턴에 속하며, 기념품 패턴이라고도 부른다.
*행동 패턴 : 객체 간 알고리즘이나 책임 분배에 관련된 패턴
- Originator
- Memento 객체를 생성하여 자신의 현재 State를 저장한다.
- State를 복원할 때 Memento를 사용한다.
- Memento
- Originator의 State를 스냅샷 형태로 가진다.
- 외부 객체가 Originator에 접근하는 것을 막는다.
- Memento를 immutable하게 만들고, 생성자 주입을 통해 Originator의 데이터를 한 번만 전달받는다.
- CareTaker
- Memento 객체를 저장하여 Originator의 State History를 가진다.
- Memento의 내용을 조작하지 않는다.
시간적 순서에 따라 Sequence Diagram으로 나타내 보면 아래와 같다.
왜 Memento Pattern을 사용할까?
실행취소 기능 (Undo)을 구현하거나, 오류가 발생한 경우 특정 객체를 복구할 때 필요하다.
이때 객체의 State (상태)를 기록해야 하는데, 외부에 저장하거나 외부에서 접근 가능하도록 하면 캡슐화를 위반하게 된다.
이러한 상황에서 메멘토 패턴을 활용하면, State를 저장할 객체를 캡슐화하고 외부에서의 접근을 제한할 수 있다.
위에서 나열한 내용을 다시 정리해보면,메멘토 패턴에서 Originator는 자신의 State를 기록하고, 이것을 Memento에 저장한다.Memento는 Originator 이외의 객체가 접근할 수 없도록 한다.Memento는 CareTaker에 저장되며, CareTaker의 제한된 인터페이스를 통해 Memento를 관리하므로 임의로 Memento의 내용을 조작할 수 없다.Originator는 Memento에 자유롭게 접근하여 원하는 State로 복원할 수 있다.
Memento Pattern의 특징
장점
- 복구 기능을 쉽게 구현할 수 있다.
- 캡슐화를 위반하지 않고 객체의 State 스냅샷을 생성할 수 있다.
- 핵심 객체와 다른 별도의 객체에 데이터를 백업할 수 있어 안전하다.
- CareTaker가 State 기록을 보관하는 역할을 담당하므로 Originator가 비대해지지 않는다.
단점
- Memento 객체를 많이 생성할수록 메모리 소비가 크다.
- Originator의 데이터 크기가 큰 경우 메모리 소비가 크다.
- CareTaker가 오래된 Memento 객체를 삭제하도록 하려면, Originator의 생명주기를 추적해야 한다.
예제 코드
게임을 Save 및 Load하는 기능을 메멘토 패턴으로 잘 구현한 예제가 있어서 소개한다.
- Reference : Pingu님 블로그
1. Originator (Game)
class Game {
var level: Int = 0
var score: Int = 0
func setLevel(level: Int) {
self.level = level
}
func setScore(score: Int) {
self.score = score
}
// Memento 생성 - Memento 생성자 주입으로 Originator 자체를 전달
func createMemento() -> SaveData {
print("Level : \(self.level), Score: \(self.score) 상태를 저장합니다.\n")
return SaveData(originator: self)
}
func printCurrentState() {
print("현재 상태 Level : \(self.level), Score: \(self.score)")
}
}
2. Memento (SaveData)
class SaveData {
private var originator: Game
private var level: Int = 0
private var score: Int = 0
// Memento 생성 시 Originator의 State를 전달받음 (이후 수정하지 않음)
init(originator: Game) {
self.originator = originator
self.level = originator.level
self.score = originator.score
}
// Originator의 State를 복원
func recover() {
self.originator.setLevel(level: self.level)
self.originator.setScore(score: self.score)
}
}
3. CareTaker (GameDataSystem)
class GameDataSystem {
private var history: [SaveData] = []
func save(memento: SaveData) { // Memento 스냅샷들을 저장
self.history.append(memento)
}
func recoverLastState() { // 가장 최근의 스냅샷을 복원
if let mementoSnapshot: SaveData = self.history.popLast() {
print("최근 저장 상태를 불러옵니다.\n")
mementoSnapshot.recover()
} else {
print("저장 기록이 없습니다.\n")
}
}
}
4. 사용부 (Main.swift)
let originator = Game()
let caretaker = GameDataSystem()
originator.setLevel(level: 5)
originator.setScore(score: 3)
// Originator가 Memento 스냅샷을 생성
let memento = originator.createMemento()
// Memento 스냅샷을 CareTaker에 저장
caretaker.save(memento: memento) // 출력 - Level : 5, Score: 3 상태를 저장합니다.
// 게임을 하다가 레벨과 점수가 이전 저장 했을 때 보다 낮아짐
originator.setLevel(level: 1)
originator.setScore(score: 1)
originator.printCurrentState() // 출력 - 현재 상태 Level : 1, Score: 1
// 레벨과 점수 복구
caretaker.recoverLastState() // 출력 - 최근 저장 상태를 불러옵니다.
originator.printCurrentState() // 출력 - 현재 상태 Level : 5, Score: 3
caretaker.recoverLastState() // 출력 - 저장 기록이 없습니다.
- Reference
- Blog > Pingu - Memento Pattern
- Blog > 무지성 - Mediator Pattern (Sequence Diagram 아이디어 감사해요)
- 위키백과 > 메멘토 패턴
🍎 포스트가 도움이 되었다면, 공감🤍 / 구독🍹 / 공유🔗 / 댓글✏️ 로 응원해주세요. 감사합니다.
'프로그래밍 철학' 카테고리의 다른 글
[toss] iOS 개발자를 위한 SIMPLICITY23 리뷰 - 디자이너와 친해지기 (7) | 2023.06.04 |
---|---|
[디자인 패턴] Singleton (0) | 2023.03.26 |
[디자인 패턴] Builder - 초기화 과정이 복잡할 때 (0) | 2023.02.20 |
[디자인 패턴] Abstract Factory - 미리 정해둔 종류로만 객체를 생성할 때 (0) | 2023.01.16 |
[디자인 패턴] Factory Method - 비슷한 종류의 타입을 찍어낼 때 (0) | 2023.01.14 |