내일배움캠프 33일차 TIL, 숙련주차 팀프로젝트

황오영·2024년 6월 3일
0

TIL

목록 보기
33/56
post-thumbnail

디자인패턴 - 명령/메멘토 패턴

  • 오늘은 명령패턴 정리를 해보려고 한다.

    참고자료 : 챌린지반 디자인패턴 강의노트

명령 패턴?

  • 메소드를 객체로 만드는것
  • 실행될 기능을 캡슐화 하면서 주어진 여러 기능들의 재사용성을 높게하는 설계 방식이다.

어떻게 하는걸까

  • 우선 코드 예시를 보자
public interface ICommand
{
    void Execute();
    void Undo();
}

public class MoveCommand : ICommand
{
    private Transform _player;
    private Vector3 _direction;
    private Vector3 _previousPosition;

    public MoveCommand(Transform player, Vector3 direction)
    {
        _player = player;
        _direction = direction;
    }

    public void Execute()
    {
        _previousPosition = _player.position;
        _player.position += _direction;
    }

    public void Undo()
    {
        _player.position = _previousPosition;
    }
}

public class CommandInvoker
{
    private Stack<ICommand> _commandHistory = new Stack<ICommand>();

    public void ExecuteCommand(ICommand command)
    {
        command.Execute();
        _commandHistory.Push(command);
    }

    public void UndoCommand()
    {
        if (_commandHistory.Count > 0)
        {
            ICommand command = _commandHistory.Pop();
            command.Undo();
        }
    }
}
  • 인터페이스를 정의하고 이를 사용한다. 이 인터페이스를 인자로 받아 실행해주는 함수를 CommandInvoker에서 선언을 해준다. (총 관리느낌)
  • 그 이후 입력을 받아주면서 사용해주기만 하면된다.
public class PlayerController : MonoBehaviour
{
    private CommandInvoker _invoker;
    private Transform _playerTransform;

    void Start()
    {
        _invoker = new CommandInvoker();
        _playerTransform = this.transform;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            ICommand moveUp = new MoveCommand(_playerTransform, Vector3.up);
            _invoker.ExecuteCommand(moveUp);
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            ICommand moveDown = new MoveCommand(_playerTransform, Vector3.down);
            _invoker.ExecuteCommand(moveDown);
        }
        if (Input.GetKeyDown(KeyCode.Z))
        {
            _invoker.UndoCommand();
        }
    }
}

전략 패턴과의 비교

  • 전략패턴은 어떻게 하는가에 중점을 둔다. 하고자 하는 것은 이미 정해져 있기 때문에 그 방법에 중점을 두고 인터페이스의 메소드에 영향을 받게 된다.
  • 반대로 커맨드 패턴은 무엇을에 중점으 둔다. 어떻게 할지에 대한 방법은 외부에서 정의를 하고 그것을 실행하는것이 중요하기 때문이다.
  • 두 전략은 모두 인터페이스를 중점으로 구현을 하는 방식이다보니 비슷하지만 위의 부분에서 서로 다르니 잘 알아두자.

메멘토 패턴?

https://refactoring.guru/ko/design-patterns/memento (참고자료)

  • 메멘토 패턴은 과거로 돌아갈 수 있도로 구현을 하는 것이다.
  • 행위 패턴에 해당되며 객체의 상태 정보를 가지는 클래스를 따로 생성한다.

구현

  • originator, memento, caretaker 3개의 개체로 구성되어 있다.
  • originator : save, restore가 memento 개체타입으로 이루어져있음
  • memento : originator가 save, restor 하려는 정보를 가진 패턴 개체
  • caretaker : memento를 리스트로 보유하여 관리하는 개체

장단점

  • 장점 : 캡슐화를 위반하지 않고 객체상태의 스냅샷을 생성할 수 있다, 케어테이커가 오리지네이터의상태의 기록을 유지하도록 하여 오리지네이터의 코드를 단순화할 수 있다.
  • 단점 : 클라이언트들이 메멘토들을 자주 생성하면 메모리 누수가 발생한다, 케어테이커들은 쓸모없는 메멘토를 파괴할 수 있도록 오리지네이터의 수명주기를 추적해야 한다, 프로그램중 몇몇 프로그램(자바,파이썬등의 동적 프로그래밍 언어)은 메멘토 내의 상태가 그대로 유지되는것을 보장할수 없다.

다른 패턴과의 비교

  • 커맨드와 메멘토 패턴을 함께 사용하면 실행 취소를 구현하기에 좋다.
  • 반복자 패턴과 사용하면 현재 순회 상태를 포착하고 롤백이 가능하다.
  • 프로토타입이 메멘토 패턴의 간단한 대안이 될 수 있다. 프로토타입 패턴은 기록에 저장하려는 객체가 간단하고 외부 리소스 링크가 없어 재설정하기 쉬운 경우에 해당한다.
    (코드예시 : 출처 위키백과 : https://ko.wikipedia.org/wiki/%EB%A9%94%EB%A9%98%ED%86%A0_%ED%8C%A8%ED%84%B4)
public class OriginalObject
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public OriginalObject(string str1, string str2)
    {
        this.String1 = str1;
        this.String2 = str2;
    }
    public void SetMemento(Memento memento)
    {
        this.String1 = memento.string1;
        this.String2 = memento.string2;
    }
    public Memento CreateMemento()
    {
        return new Memento(this.String1, this.String2);
	}
}
//Memento object
public class Memento
{
    public readonly string string1;
    public readonly string string2;
    public Memento(string str1, string str2)
    {
        this.string1 = str1;
        this.string2 = str2;
    }
}
//CareTaker Object
public class CareTaker
{
    public Memento Memento { get; set; }
}
//Client
class Program
{
    static void Main(string[] args)
    {
        // Create Originator object which container first state of "First" and "One".
        // The Memento will remember these value.
        OriginalObject original = new OriginalObject("First", "One");
        // Create first State and store to caretaker
        Memento firstMemento = original.CreateMemento();
        CareTaker caretaker = new CareTaker();
        caretaker.Memento = firstMemento;
        // Change into second state; "Second" and "Two".
        original.String1 = "Second";
        original.String2 = "Two";
        // Retrieve back first State
        original.SetMemento(caretaker.Memento);
    }
}

게임개발 숙련주차 팀프로젝트 발제

  • 게임개발 숙련주차 팀프로젝트를 시작했다. 게임은 총 3가지가 주어졌는데 3D게임으로 러닝액션게임, 퍼즐 플랫폼게임, 서바이벌 게임 3개가 주어졌는데 우리팀은 서바이벌 게임을 만들기로 했다.
  • 오늘은 기획회의 + 프레임워크 코드분석으로 보냈고 본격적인 구현은 내일부터 쭉 해볼것같다. 평소 해보고싶은 장르여서 주말도 껴있어서 열심히 해봐야겠다.
  • 간단하게 피그마를 통해 기획안도 작성해봤다. 지난번 프로젝트에 비해선 그래도 기획할게 많지않고 레퍼런스 자료들이 잘 되어있어서 잘 따라서 만들면 될 것 같다.
  • 게임 레퍼런스는 생존을 기반을 할지 전투를 기반해 할지고민이 많았는데 우선은 전투는 조금 뒤에 구현을 해보고 우선 생존과 건축시스템 위주로 진행을 해 보자고 얘기를 했다.
  • 오늘 간단하게 건축하는거 해봤는데 생각보다 어려워서 내일 또 시간투자 많이해서 꾸준히 해봐야겠다.
  • 3D다보니 에셋에서 고민이 많았는데 우선은 그냥 에셋 신경쓰지말고 기능위주로 생각해보자고 했다. 구현만 잘되면 데이터 추가는 추후의 문제라고 생각했기 때문이다.

오늘의 회고

  • 6월의 첫주인만큼 새로운 기분으로 더 열심히 한주를 보내야겠다.
  • 게임자체가 제작 난이도가 있는 게임이라서 이번엔 많은 기능을 넣어보고자 한다. 같이 하시는분들도 어느정도 유니티를 다룰줄 알아서 더 수월하게 가능할것 같다.
  • 퀘스트와 건축시스템을 만들어 보고싶어서 이 부분을 도전해야겠다.
profile
게임개발을 꿈꾸는 개발자

0개의 댓글