개발/Architecture & Design Pattern

C++ - 메멘토 패턴 (Memento Pattern)

피로물든딸기 2024. 3. 1. 23:51
반응형

 

C, C++ 전체 링크

Architecture & Design Pattern 전체 링크

 

메멘토 패턴 (Memento Pattern) - 행동 패턴

- 객체의 내부 상태를 저장하고 나중에 복원할 수 있는 패턴

- 상태를 변경하면서 객체를 수정할 때 사용한다.

 

구현

- Originator : 상태를 가지고 있는 객체, 객체의 상태를 저장하고 복원을 할 수 있다.

- Memento : 상태를 저장하는 객체, Originator 객체의 상태를 포착한다.

- Caretaker : Memento를 관리하는 객체, Memento생성, 저장, 복원을 담당한다.

 

장점

- 객체 상태를 저장하고 복원하기 때문에 이전 상태로 쉽게 되돌릴 수 있다. (실행 취소, 다시 실행 기능)

- MementoOriginator의 상태를 캡슐화하기 때문에, 원본 객체의 내부 상태를 외부로 숨길 수 있다.

- Memento 객체를 이용해 객체의 상태를 효율적으로 관리할 수 있다.

 

단점

- 상태를 저장하거나 복구할 때, 비용이 많이 들 수 있다.

- Memento 객체를 저장하기 위해 메모리를 추가적으로 사용해야 한다.

- 추가적인 클래스와 인터페이스로 구현이 복잡해진다.


텍스트 편집기

 

현재 텍스트 편집기는 새로운 내용을 입력(setText)하고, 입력된 값을 얻는 기능(getText)을 가지고 있다.

class TextEditor 
{
public:
	void setText(const string& newText) { text = newText; }
	const string& getText() const { return text; }

private:
	string text;
};

 

만약 TextEditor에 입력했던 내용을 저장하려면 vector<string> 등을 이용할 수 있을 것이다.

아래는 setText를 호출한 후, push_back으로 텍스트를 저장하는 예시다.

int main() 
{
	vector<string> save;

	TextEditor* editor = new TextEditor();
	
	editor->setText("Hello, world!");
	save.push_back(editor->getText());

	...
}

 

vector에 편집한 내용을 저장하였기 때문에 indexback() / pop_back() 등으로 이전 값을 복원할 수 있다.

	int index = save.size() - 1;
	cout << "Current text: " << save[index--] << endl;

	/* ---------------------------------------------------- */

	cout << "Current text: " << save.back() << endl;
	save.pop_back();

 

예시 코드는 다음과 같다.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class TextEditor 
{
public:
	void setText(const string& newText) { text = newText; }
	const string& getText() const { return text; }

private:
	string text;
};

int main() 
{
	vector<string> save;

	TextEditor* editor = new TextEditor();
	
	editor->setText("Hello, world!");
	save.push_back(editor->getText());

	editor->setText("I hate c++");
	//save.push_back(editor->getText());

	editor->setText("My name is ...");
	save.push_back(editor->getText());

	editor->setText("bloodstrawberry!");
	save.push_back(editor->getText());

	editor->setText("Nice to meet you.");
	save.push_back(editor->getText());

	/* ---------------------------------------------------- */

	int index = save.size() - 1;
	cout << "Current text: " << save[index--] << endl;
	cout << "Current text: " << save[index--] << endl;
	cout << "Current text: " << save[index--] << endl;
	cout << "Current text: " << save[index--] << endl;

	/* ---------------------------------------------------- */

	cout << endl;

	cout << "Current text: " << save.back() << endl;
	save.pop_back();

	cout << "Current text: " << save.back() << endl;
	save.pop_back();

	cout << "Current text: " << save.back() << endl;
	save.pop_back();

	cout << "Current text: " << save.back() << endl;
	save.pop_back();

	return 0;
}


메멘토 패턴 적용

 

메멘토 패턴을 이용하면 상태를 저장하고 복원하는 행동을 Originator에게 위임한다.

그리고 상태를 따로 저장하는 역할을 맡는 객체를 Memento라고 한다.

마지막으로 Caretaker는 메멘토들을 저장하고, 복원하는 역할을 맡는다.

 

이렇게 역할을 나눠서 핵심적인 객체의 캡슐화를 유지할 수 있다.

 

메멘토 클래스는 다음과 같이 정의한다.

여기서 state는 에디터가 입력한 text가 된다.

class Memento
{
public:
	Memento(const string& s) : state(s) {}
	const string& getState() const { return state; }

private:
	string state;
};

 

TextEditorOriginator가 된다.

snapshot을 이용해 Memento를 저장한다.

// Originator 
class TextEditor
{
public:
	void setText(const string& newText) { text = newText; }
	const string& getText() const { return text; }
	Memento* snapshot() const { return new Memento(text); }
	void restoreMemento(Memento* memento) { text = memento->getState(); }

private:
	string text;
};

 

CaretakerMemento를 관리하게 된다. 

snapshotMemento 객체를 addMemento로 저장한다.

여기서는 getMemento에서 index를 받아서 이전 상태를 부르거나,

rollback을 활용하여 바로 직전 상태를 얻도록 하였다.

class TextEditorCaretaker
{
public:
	void addMemento(Memento* m) { mementos.push_back(m); }
	Memento* getMemento(int index) const { return mementos[index]; }

	Memento* rollback()
	{
		Memento* ret = mementos.back();
		mementos.pop_back();
		return ret;
	}

private:
	vector<Memento*> mementos;
};

 

특정 상태를 저장하는 방법은 다음과 같다.

	editor->setText("Hello, world!");
	caretaker->addMemento(editor->snapshot());

 

Index로 이전 상태를 복원하는 방법은 다음과 같다. (가능한 index만 넣는다고 가정하였다.)

	editor->restoreMemento(caretaker->getMemento(3));
	cout << "Current text: " << editor->getText() << endl;

 

바로 직전 상태로 복구하는 방법은 다음과 같다.

	editor->restoreMemento(caretaker->rollback());
	cout << "Current text: " << editor->getText() << endl;

 

전체 코드는 다음과 같다.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Memento 
{
public:
	Memento(const string& s) : state(s) {}
	const string& getState() const { return state; }

private:
	string state;
};

// Originator 
class TextEditor 
{
public:
	void setText(const string& newText) { text = newText; }
	const string& getText() const { return text; }
	Memento* createMemento() const { return new Memento(text); }
	void restoreMemento(Memento* memento) { text = memento->getState(); }

private:
	string text;
};

class TextEditorCaretaker 
{
public:
	void addMemento(Memento* m) { mementos.push_back(m); }
	Memento* getMemento(int index) const { return mementos[index]; }

	Memento* rollback() 
	{
		Memento* ret = mementos.back();
		mementos.pop_back();
		return ret;
	}

private:
	vector<Memento*> mementos;
};

int main() 
{
	TextEditor* editor = new TextEditor();
	TextEditorCaretaker* caretaker = new TextEditorCaretaker();

	editor->setText("Hello, world!");
	caretaker->addMemento(editor->createMemento());

	editor->setText("I hate c++");
	//caretaker->addMemento(editor->createMemento());

	editor->setText("My name is ...");
	caretaker->addMemento(editor->createMemento());

	editor->setText("bloodstrawberry!");
	caretaker->addMemento(editor->createMemento());

	editor->setText("Nice to meet you.");
	caretaker->addMemento(editor->createMemento());

	/* ---------------------------------------------------- */

	editor->restoreMemento(caretaker->getMemento(3));
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->getMemento(2));
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->getMemento(1));
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->getMemento(0));
	cout << "Current text: " << editor->getText() << endl;

	/* ---------------------------------------------------- */

	cout << endl;

	editor->restoreMemento(caretaker->rollback());
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->rollback());
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->rollback());
	cout << "Current text: " << editor->getText() << endl;

	editor->restoreMemento(caretaker->rollback());
	cout << "Current text: " << editor->getText() << endl;

	return 0;
}

반응형