9. Command Pattern ( 커맨드 패턴 C# )

|

[읽기전에]

  UML 다이어그램 : UML클래스 다이어그램 기본상식 http://hongjinhyeon.tistory.com/25 

  포스팅되는 디자인 패턴의 예는 스타크래프트를 기본으로 하였습니다 : 디자인 패턴을 시작하며 http://hongjinhyeon.tistory.com/24


<기본 정의 >


1. Command Pattern 정의

 

  -커맨드 패턴을 이용하면 요구 사항을 객체로 갭슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어 넣을수 있다.

   또한 요청 내역을 큐에 저장하거나 로그로 기록 할 수 있으며, 작업취소 기능도 지원이 가능하다.

   (Encapsulate a request as an object, thereby letting you parameterize clients with different requests,

    queue or log requests, and support undoable operations.)



2. UML Diagram





3. 사용용도 및 장점


 -사용자의 요청을 객체화 시킴으로써 ( 객체 안에 작업에 필요한 모든 내용이 들어가있음 ), 그 객체만 있으면 해당 커맨드가 어떤

  작업을 수행했는지 알 수가있다. 그럼으로 해서 다시 취소작업을 수행한다거나 로그기능을 구현이 가능하다.


 -커맨트 패턴은 커맨드에 알맞은 파라미터만 전달해주면 알아서 결과가 처리가된다. (예) 계산기

   요청만 하면 내부적으로 알고리즘에 의해서 자동으로 처리가 된다.



4. 소스


using System;
 
namespace DoFactory.GangOfFour.Command.Structural
{
  /// <summary>
  /// MainApp startup class for Structural
  /// Command Design Pattern.
  /// </summary>
  class MainApp
  {
    /// <summary>
    /// Entry point into console application.
    /// </summary>
    static void Main()
    {
      // Create receiver, command, and invoker
      Receiver receiver = new Receiver();
      Command command = new ConcreteCommand(receiver);
      Invoker invoker = new Invoker();
 
      // Set and execute command
      invoker.SetCommand(command);
      invoker.ExecuteCommand();
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
  /// <summary>
  /// The 'Command' abstract class
  /// </summary>
  abstract class Command
  {
    protected Receiver receiver;
 
    // Constructor
    public Command(Receiver receiver)
    {
      this.receiver = receiver;
    }
 
    public abstract void Execute();
  }
 
  /// <summary>
  /// The 'ConcreteCommand' class
  /// </summary>
  class ConcreteCommand : Command
  {
    // Constructor
    public ConcreteCommand(Receiver receiver) :
      base(receiver)
    {
    }
 
    public override void Execute()
    {
      receiver.Action();
    }
  }
 
  /// <summary>
  /// The 'Receiver' class
  /// </summary>
  class Receiver
  {
    public void Action()
    {
      Console.WriteLine("Called Receiver.Action()");
    }
  }
 
  /// <summary>
  /// The 'Invoker' class
  /// </summary>
  class Invoker
  {
    private Command _command;
 
    public void SetCommand(Command command)
    {
      this._command = command;
    }
 
    public void ExecuteCommand()
    {
      _command.Execute();
    }
  }
}

 -main 부분이 Client에 해당합니다.



5. 실행결과


Called Receiver.Action()




< 실제 적용 >


1. UML Diagram





2. 사용용도 및 장점




 -스타크래프트에서 커맨드 패턴을 찾기가 힘들었습니다. Redo, Undo가 되는게 없더군요. 그와중에 발견한 스타크래프트 맵을 만들어주는 에디터(빙고)

 -맵 에디터에서 유닛들을 배치하거나 지형(언덕, 물, 미네랄등)을 배치하고 다시 Undo도 할수잇고 Redo도 가능합니다.

 -하나 하나의 명령어들을 객체화함으로써 Undo나 Redo가 가능하게 됩니다.



3. 소스


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

namespace Command
{
    class Program
    {
        static void Main(string[] args)
        {
            MapEditor mapEditor = new MapEditor();

            mapEditor.Create("마린1", "100", "200");
            mapEditor.Create("마린2", "200", "400");
            mapEditor.Create("마린3", "300", "600");
            mapEditor.Delete("마린2", "300", "600");

            mapEditor.Undo(3);
            mapEditor.Redo(3);

            Console.ReadKey();

        }

        /// <summary>
        /// Receiver 클래스
        /// </summary>
        class UnitManger
        {

            public void CreatUnit(string _unitName, string _pointX, string _pointY)
            {
                Console.WriteLine("[{0}] 유닛을 X좌표 {1} , Y좌표 {2} 위치에 생성합니다.", _unitName, _pointX, _pointY);
            }

            public void DeleteUnit(string _unitName, string _pointX, string _pointY)
            {
                Console.WriteLine("[{0}] 유닛을 X좌표 {1} , Y좌표 {2} 위치에서 삭제합니다.", _unitName, _pointX, _pointY);
            }

        }

        /// <summary>
        /// 추상  Command 생성
        /// </summary>
        abstract class Command
        {
            public string unitName;
            public string pointX;
            public string pointY;

            public abstract void Execute();
        }

        /// <summary>
        /// 유닛 생성 Concreate 객체 생성
        /// </summary>
        class CreatCommand : Command
        {

            private UnitManger unitManger;

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="_unitManger"></param>
            public CreatCommand(UnitManger _unitManger, string _unitName, string _pointX, string _pointY)
            {
                this.unitManger = _unitManger;
                this.unitName = _unitName;
                this.pointX = _pointX;
                this.pointY = _pointY;
            }

            public override void Execute()
            {
                unitManger.CreatUnit(this.unitName, this.pointX, this.pointY);
            }
           
        }

        /// <summary>
        ///  유닛 삭제 Concreate 객체 생성
        /// </summary>
        class DeleteCommand : Command
        {
            private UnitManger unitManger;

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="_unitManger"></param>
            public DeleteCommand(UnitManger _unitManger, string _unitName, string _pointX, string _pointY)
            {
                this.unitManger = _unitManger;
                this.unitName = _unitName;
                this.pointX = _pointX;
                this.pointY = _pointY;
            }

            public override void Execute()
            {
                unitManger.DeleteUnit(this.unitName, this.pointX, this.pointY);
            }

        }


        class MapEditor
        {
            private UnitManger _uintManger = new UnitManger();
            private List<Command> _commands = new List<Command>();
            private int _current = 0;

            public void Create(string _unitName, string _pointX, string _pointY)
            {
                Command command = new CreatCommand(_uintManger, _unitName, _pointX, _pointY);
                command.Execute();

                _commands.Add(command);
                _current++;
            }

            public void Delete(string _unitName, string _pointX, string _pointY)
            {
                Command command = new DeleteCommand(_uintManger, _unitName, _pointX, _pointY);
                command.Execute();

                _commands.Add(command);
                _current++;
            }


            public void Redo(int levels)
            {
              Console.WriteLine("\n---- Redo {0} levels ", levels);
            
              for (int i = 0; i < levels; i++)
              {
                if (_current <= _commands.Count - 1)
                {
                  Command command = _commands[_current++];
                  command.Execute();
                }
              }
            }
 
            public void Undo(int levels)
            {
              Console.WriteLine("\n---- Undo {0} levels ", levels);
              // Perform undo operations
              for (int i = 0; i < levels; i++)
              {
                if (_current > 0)
                {
                    Command command = _commands[--_current] as Command;

                    if ((command as CreatCommand) != null)
                    {
                        //생성일때 삭제처리
                        Command commandTemp = new DeleteCommand(_uintManger, command.unitName, command.pointX, command.pointY);
                        commandTemp.Execute();
                    }
                    else
                    {
                        //삭제일때 생성처리
                        Command commandTemp = new CreatCommand(_uintManger, command.unitName, command.pointX, command.pointY);
                        commandTemp.Execute();
                    }

                }

              }

            }

        }

    }
}


4. 실행결과





5. 피드백


 -커맨드 패턴의 핵심은 사용자의 요구사항 자체를 객체로 캡슐화 하는것이다.

 -처음 UML 다이어그램을 봤을때 이게 먼가할정도로 좀 꼬여서 서로를 생성하고 소유하는거 같습니다.

 -커맨드를 주문서, 인보커를 종업원, 리시버를 주방정 정도로 생각하시면 됩니다.

 


[참조]

 -http://www.dofactory.com

 -Head First Design Patterns

And