본문 바로가기

Design Pattern

[Design Pattern] 11. Memento Pattern(메멘토 패턴) C#

728x90

객체의 데이터에 대한 기록을 하고 이전 상태로 되돌리게 해주는 패턴입니다.

 

행동 패턴(Behavioral Design Patterns)중 하나이죠.

 

장점

  • 캡슐화를 하고 객체의 상태의 스냅샷들을 저장할 수 있습니다.
  • CareTaker가 Originator의 상태의 기록을 유지하도록 하여 Originator의 코드를 단순화 시킬 수 있습니다.
  • 메멘토들을 자주 생성하면 앱이 많은 리소스를 사용할 수 있습니다.

단점

  • CareTaker가 사용하지 않는 메멘토들을 파괴할 수 있도록 Originator의 수명주기를 알아야 합니다.
  • 대부분의 동적 프로그래밍 언어에서는 메멘토가 그대로 유지된다고 보장할 수 없습니다.

 

C#으로 Console 프로젝트를 하나 생성해서 할 것입니다.

 

목표는 문자열을 넣었다가 이전 기록들을 출력하고 복구해서 출력하겠습니다.

 

프로젝트의 tree구조는 아래와 같습니다.

Memento (Project)

|- Program.cs (Main)

|- IMemento.cs (Memento Interface)

|- Memento.cs (Concreate Memento)

|- Originator.cs (Originator)

|- Caretaker.cs (Caretaker)

 

 

IMemento.cs 

using System;

namespace Memento
{
    public interface IMemento
    {
        string GetState();

        DateTime GetDate();
    }
}

상태와 시간을 가져올 수 있게 구성해줍니다.


Memento.cs 

using System;

namespace Memento
{
    public class Memento : IMemento
    {
        private string state;

        private DateTime date;

        public Memento(string state)
        {
            this.state = state;
            this.date = DateTime.Now;
        }

        public string GetState()
        {
            return this.state;
        }

        public DateTime GetDate()
        {
            return this.date;
        }
    }
}

IMemento를 구현하면서 생성자에 생성시간을 알 수 있도록 내용을 추가해줍니다.


Originator.cs

namespace Memento
{
    public class Originator
    {
        private string state;

        public Memento Save()
        {
            return new Memento(state);
        }

        public void Restore(IMemento memento)
        {
            this.state = memento.GetState();
        }

        public string GetState()
        {
            return this.state;
        }

        public void SetState(string state)
        {
            this.state = state;
        }
    }
}

Originator가 만들거나 관리할 수 있도록 저장, 복구, 상태 가져오기를 만들어 줍니다.


Caretaker.cs

using System;
using System.Collections.Generic;
using System.Linq;

namespace Memento
{
    public class Caretaker
    {
        private List<IMemento> _mementos = new List<IMemento>();

        private Originator _originator = null;

        public Caretaker(Originator originator)
        {
            _originator = originator;
        }

        public void Backup()
        {
            _mementos.Add(this._originator.Save());
        }

        public void Undo()
        {
            if (_mementos.Count == 0)
            {
                return;
            }

            IMemento memento = _mementos.Last();
            _mementos.Remove(memento);

            Console.WriteLine(memento.GetState());

            try
            {
                _originator.Restore(memento);
            }
            catch (Exception ex)
            {
                Undo();
            }
        }

        public void ShowHistory()
        {
            foreach (IMemento memento in this._mementos)
            {
                Console.WriteLine($"[{memento.GetDate().ToString("hh:mm:ss:ffff")}] {memento.GetState()}");
            }
        }
    }

}

먼저 IMemento List를 만들어 저장할 수 있도록 구성해준 뒤 생성자에 originator를 입력받게 만들고 백업, 실행 취소, 기록 조회를 구현해줍니다.

 

Program.cs

using System;
using System.Threading;

namespace Memento
{
    class Program
    {
        static void Main(string[] args)
        {
            Originator originator = new Originator();
            Caretaker caretaker = new Caretaker(originator);

            originator.SetState("1");
            caretaker.Backup();

            Thread.Sleep(3000);

            originator.SetState(originator.GetState() + "2");
            caretaker.Backup();

            Thread.Sleep(1000);

            originator.SetState(originator.GetState() + "3");
            caretaker.Backup();

            Console.WriteLine("Show history");
            caretaker.ShowHistory();

            Console.WriteLine("\nUndo");
            caretaker.Undo();

            Console.WriteLine("\nUndo");
            caretaker.Undo();

            Console.WriteLine("\nUndo");
            caretaker.Undo();

            Console.WriteLine("\nUndo");
            caretaker.Undo();
        }
    }
}

Originator와 CareTacker를 만들어 주고 originator에 state를 이전 상태에 추가하는 형태로 수정하고 CareTracker에 백업을 해줍니다.

기록 조회에 시간을 확인하는데 시간확인을 위해 임의로 Thread Sleep을 줬습니다.

 

이제 기록을 출력하고 실행 취소한 결과를 출합니다.

 

실행 결과

결과를 확인 해보시면 원하는 대로 출력됨을 확인 하실 수 있습니다.

 

그렇게 많이 쓰는 패턴은 아닙니다만

간혹 한번씩 사용될 수 있습니다.

 

이번 글을 기점으로 행동 패턴 10개가 끝났습니다.

 

모두 수고많으셨습니다.

다음부턴 구조 패턴에 대한 글로 찾아오겠습니다.