
Memento 1. Memento
Memento 패턴은 객체의 상태를 저장하고 나중에 복원할 수 있도록 하는 디자인 패턴입니다. 이를 통해 객체의 상태를 원래 상태로 되돌리거나 특정 시점의 상태로 복원할 수 있습니다.
Code Smell
어떤 객체의 상태가 자주 변경되거나, 특정 시점의 상태로 되돌려야 하는 요구사항이 있을 때 Code Smell이 발생할 수 있습니다. 예를 들어, 게임에서 로봇의 위치가 자주 바뀌는 경우, 사용자가 실수로 이동한 위치를 되돌리고 싶을 때 상태를 저장해두는 것이 필요할 수 있습니다.
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
public class Robot
{
public Vector3 Position { get; set; }
private Stack<Vector3> positionHistory = new Stack<Vector3>();
public Robot(Vector3 position)
{
Position = position;
}
// 상태 변경 전 현재 상태를 저장
public void Move(Vector3 newPosition)
{
// 상태 저장
positionHistory.Push(Position);
// 상태 변경
Position = newPosition;
}
// 이전 상태로 되돌리기
public void Undo()
{
if (positionHistory.Count > 0)
{
// 이전 상태 복원
Position = positionHistory.Pop();
}
}
}
문제점
-
상태 저장 및 복원 로직의 결합: 상태를 저장하는 로직과 복원하는 로직이 Robot 클래스 내부에 직접 구현되어 있습니다. 이로 인해 클래스의 책임이 증가하고, 상태 저장 및 복원과 관련된 코드가 중복될 가능성이 있습니다.
-
확장성 부족: 만약 상태를 더 복잡하게 관리해야 하거나 여러 종류의 상태를 관리해야 한다면, 이 방식은 매우 비효율적입니다. 새로운 상태를 추가할 때마다 Robot 클래스 내부를 수정해야 합니다.
-
유지보수 어려움: 상태 복원과 저장의 로직이 한 곳에 몰려 있어서, 코드가 길어지면 코드 유지보수가 어려워질 수 있습니다. 또한, 상태를 여러 번 되돌리거나, 복잡한 상태를 관리할 때 코드의 복잡성이 기하급수적으로 늘어날 수 있습니다.
Memento 패턴의 코드 구현
Memento 패턴은 객체의 상태를 캡슐화하여 저장하고, 필요한 시점에 상태를 복원할 수 있도록 도와줍니다. 이 패턴에서는 RobotStateManager라는 관리자가 상태를 저장하고 관리합니다. 이를 통해 상태 저장 로직과 상태 복원 로직을 객체 외부로 분리할 수 있습니다.
코드 예시
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
32
public class Robot
{
public Vector3 Position { get; set; }
public Robot(Vector3 position)
{
Position = position;
}
// 상태를 저장하는 Memento 생성
public Memento CreateMemento()
{
return new Memento(Position);
}
// 저장된 상태로 복원
public void SetState(Memento memento)
{
Position = memento.Position;
}
// Memento 클래스는 Robot의 상태를 저장
public class Memento
{
public Vector3 Position { get; private set; }
public Memento(Vector3 position)
{
Position = position;
}
}
}
RobotStateManager: 상태를 관리하는 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RobotStateManager
{
private List<Robot.Memento> mementoList = new();
public void AddMemento(Robot.Memento memento)
{
mementoList.Add(memento);
}
public Robot.Memento GetMemento(int index)
{
return mementoList[index];
}
}
사용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Program
{
static void Main()
{
Robot robot = new Robot(new Vector3(0, 0, 0));
RobotStateManager stateManager = new();
// 처음 상태 저장
stateManager.AddMemento(robot.CreateMemento());
// 상태 변경
robot.Position = new Vector3(0, 0, 1);
stateManager.AddMemento(robot.CreateMemento());
// 첫 번째 상태로 복원
robot.SetState(stateManager.GetMemento(0));
}
}
이 코드에서 Robot 객체의 상태는 Vector3
로 저장되며, RobotStateManager 객체는 상태를 관리하고 저장된 상태를 복원할 수 있습니다.
개선된 점
-
책임 분리: 상태 저장과 복원의 책임이 RobotStateManager 로 분리되었습니다. Robot은 자신의 상태만 관리하고, 상태 저장 및 복원은 외부 객체가 처리합니다.
-
확장성: 여러 상태를 관리하거나 추가할 때, Robot 클래스가 아닌 RobotStateManager 에서 상태 관리 로직을 확장할 수 있습니다.
-
유지보수성: 상태 관리 로직이 Robot 클래스에서 분리되어, 각 클래스의 역할이 명확해지고 유지보수가 쉬워집니다.
단점
-
메모리 사용 증가: 객체의 상태를 계속해서 저장해야 하므로, 저장된 상태가 많아질수록 메모리 사용량이 늘어날 수 있습니다.
-
상태 저장 비용: 객체의 상태가 매우 복잡한 경우, 상태를 저장하고 복원하는 데 드는 비용이 증가할 수 있습니다.
-
상태 복원의 한계: Memento 패턴은 객체의 상태만을 저장하므로, 외부 환경의 변화를 반영하지 못할 수 있습니다. 상태 복원이 객체 외부의 상태에 의존하는 경우 한계가 있습니다.
결론
Memento 패턴은 객체의 상태를 저장하고 복원하는 기능을 구현할 때 매우 유용한 패턴입니다. 특히 Undo/Redo 기능이나 객체의 상태를 관리해야 하는 상황에서 적합합니다. 하지만, 저장된 상태가 많아질수록 메모리 사용량이 증가할 수 있다는 단점도 있으므로, 적절한 상황에서 신중히 사용해야 합니다.