티스토리 뷰

반응형

안녕하세요 Pingu입니다!🐧

 

지난 글에서는 행동 패턴 중 하나인 Mediator Pattern(중재자)에 대해 알아봤었는데요, 이번 글에서는 또 다른 행동 패턴 중 하나인 Memento Pattern(메멘토)에 대해 알아보도록 하겠습니다.

 

메멘토 패턴이란?

Memento Pattern은 구현의 세부 사항을 공개하지 않고, 즉 캡슐화를 위반하지 않고 객체의 이전 상태를 저장하고 복원할 수 있는 디자인 패턴입니다.

  • Originator
    • 자체적으로 현재의 state를 저장하는 Memento 객체를 생성할 수 있습니다.
    • State를 복원하기 위해 Memento를 사용합니다.
  • Memento
    • Originator의 state에 대한 스냅샷 역할을 하는 객체입니다.
    • Originator 이외 객체의 접근으로부터 보호합니다.
    • Memento를 immutable하게 만들고 생성자를 통해 데이터를 한 번만 전달합니다.
  • Care Taker
    • Memento 객체들을 저장하여 Originator의 동작을 추적합니다.
    • Memento의 내용을 조작하거나 검토하지 않습니다.

메멘토 패턴은 언제 쓰나요?

프로그래밍을 하다 보면 객체의 내부 상태를 기록해둬야 할 때가 있습니다. 이러한 상황에는 오류에서 복구할 수 있도록 하거나 텍스트 편집기에서 실행 취소 기능을 구현할 때 필요할 거예요. 이러한 상황에서 객체의 상태를 기록해줘야 하는데 이를 외부에 저장하거나 다른 객체에서 접근 가능하도록 하면 캡슐화를 위반하게 됩니다. 즉 상태를 저장할 클래스의 모든 내부 정보를 노출하지 않고 이러한 상태에 접근하는 것을 제한해줘야 하는데 이럴 때 메멘토 패턴을 사용할 수 있습니다.

 

Memento Pattern에서 State를 기록하는 Originator 객체가 State를 기록하고 이를 Memento 객체에 저장합니다. Memento 객체는 Originator 객체를 제외하고는 접근 할 수 없도록 만듭니다. 이러한 Memento 객체들은 Caretaker라는 객체에 저장되며 Caretaker는 제한된 인터페이스를 통해서 Memento 객체를 다루기 때문에 Memento 객체의 내부를 조작할 수 없습니다. 하지만 Originator는 Memento 객체에 자유롭게 접근할 수 있기 때문에 원하는 State로 복원할 수 있게 됩니다.

메멘토 패턴의 결과

장점

  • 캡슐화를 위반하지 않고 객체의 state 스냅샷을 생성할 수 있습니다.
  • Caretaker가 Originator의 State 기록을 유지하므로 Originator의 코드가 단순화됩니다.

단점

  • 클라이언트가 Memento 객체를 너무 많이 생성하면 메모리가 많이 사용됩니다.
  • Caretaker는 오래된 memento 객체를 삭제 할 수 있도록 Originator의 생명 주기를 추적해야 합니다. 즉 자원을 소비해야 합니다.
  • 몇몇 언어에서는 Originator만 Memento 객체에 접근할 수 있도록 만드는 것이 어려울 수 있습니다.

예제

그럼 Memento Pattern을 Swift 언어로 간단하게 구현해보도록 하겠습니다.

여러 가지 게임에서 Save, Load 기능이 있는데, 게임을 save 하고 load 하는 기능을 메멘토 패턴으로 구현하기에 재밌어 보여서 한 번 구현해볼게요.

 

먼저 Originator 역할을 하는 Game 클래스를 정의합니다.

// Originator
class Game {
    var level: Int = 0
    var score: Int = 0
    
    func setLevel(level: Int) {
        self.level = level
    }
    
    func setScore(score: Int) {
        self.score = score
    }
    
    func makeSnapshot() -> SaveData {
        print("Level : \(self.level), Score: \(self.score) 상태를 저장합니다.\n")
        return SaveData(originator: self)
    }
    
    func printCurrentState() {
        print("현재 상태 Level : \(self.level), Score: \(self.score)")
    }
}

방금 정의한 Game의 객체 상태를 저장할 Memento 클래스를 하나 정의합니다.

// Memento
class SaveData {
    var originator: Game
    var level: Int = 0
    var score: Int = 0
    
    init(originator: Game) {
        self.originator = originator
        self.level = originator.level
        self.score = originator.score
    }
    
    func load() {
        self.originator.setLevel(level: self.level)
        self.originator.setScore(score: self.score)
    }
}

Memento 객체 역할을 하는 SaveData 클래스에는 load라는 메서드를 만들어 해당 memento 객체의 상태로 originator의 상태를 복원할 수 있도록 만들었어요.

 

마지막으로 Caretaker 클래스를 정의해줍니다.

// Caretaker
class GameDataSystem {
    private var history: [SaveData] = []
    
    func save(snapshot: SaveData) {
        self.history.append(snapshot)
    }
    
    func load() {
        if let snapshot = self.history.popLast() {
            print("최근 저장 상태를 불러옵니다.\n")
            snapshot.load()
        } else {
            print("저장 기록이 없습니다.\n")
        }
    }
}

Caretaker 객체에는 Originator가 만든 Memento 객체들을 저장하고 복원을 위한 load 메서드를 정의해줍니다.

 

이렇게 만든 클래스들을 직접 사용하면 아래와 같이 실행됩니다.

객체의 상태를 잘 저장하고 이를 잘 불러오는 것을 볼 수 있습니다.

 

이렇게 Memento Pattern을 알아보고 간단하게 구현도 해봤습니다.

혹시라도 틀린 부분이 있다면 알려주시면 감사하겠습니다.

 

전체 코드는 여기에서 볼 수 있습니다.

 

감사합니다.

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함