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. 소스


01.using System;
02.  
03.namespace DoFactory.GangOfFour.Command.Structural
04.{
05.  /// <summary>
06.  /// MainApp startup class for Structural
07.  /// Command Design Pattern.
08.  /// </summary>
09.  class MainApp
10.  {
11.    /// <summary>
12.    /// Entry point into console application.
13.    /// </summary>
14.    static void Main()
15.    {
16.      // Create receiver, command, and invoker
17.      Receiver receiver = new Receiver();
18.      Command command = new ConcreteCommand(receiver);
19.      Invoker invoker = new Invoker();
20.  
21.      // Set and execute command
22.      invoker.SetCommand(command);
23.      invoker.ExecuteCommand();
24.  
25.      // Wait for user
26.      Console.ReadKey();
27.    }
28.  }
29.  
30.  /// <summary>
31.  /// The 'Command' abstract class
32.  /// </summary>
33.  abstract class Command
34.  {
35.    protected Receiver receiver;
36.  
37.    // Constructor
38.    public Command(Receiver receiver)
39.    {
40.      this.receiver = receiver;
41.    }
42.  
43.    public abstract void Execute();
44.  }
45.  
46.  /// <summary>
47.  /// The 'ConcreteCommand' class
48.  /// </summary>
49.  class ConcreteCommand : Command
50.  {
51.    // Constructor
52.    public ConcreteCommand(Receiver receiver) :
53.      base(receiver)
54.    {
55.    }
56.  
57.    public override void Execute()
58.    {
59.      receiver.Action();
60.    }
61.  }
62.  
63.  /// <summary>
64.  /// The 'Receiver' class
65.  /// </summary>
66.  class Receiver
67.  {
68.    public void Action()
69.    {
70.      Console.WriteLine("Called Receiver.Action()");
71.    }
72.  }
73.  
74.  /// <summary>
75.  /// The 'Invoker' class
76.  /// </summary>
77.  class Invoker
78.  {
79.    private Command _command;
80.  
81.    public void SetCommand(Command command)
82.    {
83.      this._command = command;
84.    }
85.  
86.    public void ExecuteCommand()
87.    {
88.      _command.Execute();
89.    }
90.  }
91.}

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



5. 실행결과


Called Receiver.Action()




< 실제 적용 >


1. UML Diagram





2. 사용용도 및 장점




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

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

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



3. 소스


001.using System;
002.using System.Collections.Generic;
003.using System.Linq;
004.using System.Text;
005. 
006.namespace Command
007.{
008.    class Program
009.    {
010.        static void Main(string[] args)
011.        {
012.            MapEditor mapEditor = new MapEditor();
013. 
014.            mapEditor.Create("마린1", "100", "200");
015.            mapEditor.Create("마린2", "200", "400");
016.            mapEditor.Create("마린3", "300", "600");
017.            mapEditor.Delete("마린2", "300", "600");
018. 
019.            mapEditor.Undo(3);
020.            mapEditor.Redo(3);
021. 
022.            Console.ReadKey();
023. 
024.        }
025. 
026.        /// <summary>
027.        /// Receiver 클래스
028.        /// </summary>
029.        class UnitManger
030.        {
031. 
032.            public void CreatUnit(string _unitName, string _pointX, string _pointY)
033.            {
034.                Console.WriteLine("[{0}] 유닛을 X좌표 {1} , Y좌표 {2} 위치에 생성합니다.", _unitName, _pointX, _pointY);
035.            }
036. 
037.            public void DeleteUnit(string _unitName, string _pointX, string _pointY)
038.            {
039.                Console.WriteLine("[{0}] 유닛을 X좌표 {1} , Y좌표 {2} 위치에서 삭제합니다.", _unitName, _pointX, _pointY);
040.            }
041. 
042.        }
043. 
044.        /// <summary>
045.        /// 추상  Command 생성
046.        /// </summary>
047.        abstract class Command
048.        {
049.            public string unitName;
050.            public string pointX;
051.            public string pointY;
052. 
053.            public abstract void Execute();
054.        }
055. 
056.        /// <summary>
057.        /// 유닛 생성 Concreate 객체 생성
058.        /// </summary>
059.        class CreatCommand : Command
060.        {
061. 
062.            private UnitManger unitManger;
063. 
064.            /// <summary>
065.            /// 생성자
066.            /// </summary>
067.            /// <param name="_unitManger"></param>
068.            public CreatCommand(UnitManger _unitManger, string _unitName, string _pointX, string _pointY)
069.            {
070.                this.unitManger = _unitManger;
071.                this.unitName = _unitName;
072.                this.pointX = _pointX;
073.                this.pointY = _pointY;
074.            }
075. 
076.            public override void Execute()
077.            {
078.                unitManger.CreatUnit(this.unitName, this.pointX, this.pointY);
079.            }
080.            
081.        }
082. 
083.        /// <summary>
084.        ///  유닛 삭제 Concreate 객체 생성
085.        /// </summary>
086.        class DeleteCommand : Command
087.        {
088.            private UnitManger unitManger;
089. 
090.            /// <summary>
091.            /// 생성자
092.            /// </summary>
093.            /// <param name="_unitManger"></param>
094.            public DeleteCommand(UnitManger _unitManger, string _unitName, string _pointX, string _pointY)
095.            {
096.                this.unitManger = _unitManger;
097.                this.unitName = _unitName;
098.                this.pointX = _pointX;
099.                this.pointY = _pointY;
100.            }
101. 
102.            public override void Execute()
103.            {
104.                unitManger.DeleteUnit(this.unitName, this.pointX, this.pointY);
105.            }
106. 
107.        }
108. 
109. 
110.        class MapEditor
111.        {
112.            private UnitManger _uintManger = new UnitManger();
113.            private List<Command> _commands = new List<Command>();
114.            private int _current = 0;
115. 
116.            public void Create(string _unitName, string _pointX, string _pointY)
117.            {
118.                Command command = new CreatCommand(_uintManger, _unitName, _pointX, _pointY);
119.                command.Execute();
120. 
121.                _commands.Add(command);
122.                _current++;
123.            }
124. 
125.            public void Delete(string _unitName, string _pointX, string _pointY)
126.            {
127.                Command command = new DeleteCommand(_uintManger, _unitName, _pointX, _pointY);
128.                command.Execute();
129. 
130.                _commands.Add(command);
131.                _current++;
132.            }
133. 
134. 
135.            public void Redo(int levels)
136.            {
137.              Console.WriteLine("\n---- Redo {0} levels ", levels);
138.             
139.              for (int i = 0; i < levels; i++)
140.              {
141.                if (_current <= _commands.Count - 1)
142.                {
143.                  Command command = _commands[_current++];
144.                  command.Execute();
145.                }
146.              }
147.            }
148.  
149.            public void Undo(int levels)
150.            {
151.              Console.WriteLine("\n---- Undo {0} levels ", levels);
152.              // Perform undo operations
153.              for (int i = 0; i < levels; i++)
154.              {
155.                if (_current > 0)
156.                {
157.                    Command command = _commands[--_current] as Command;
158. 
159.                    if ((command as CreatCommand) != null)
160.                    {
161.                        //생성일때 삭제처리
162.                        Command commandTemp = new DeleteCommand(_uintManger, command.unitName, command.pointX, command.pointY);
163.                        commandTemp.Execute();
164.                    }
165.                    else
166.                    {
167.                        //삭제일때 생성처리
168.                        Command commandTemp = new CreatCommand(_uintManger, command.unitName, command.pointX, command.pointY);
169.                        commandTemp.Execute();
170.                    }
171. 
172.                }
173. 
174.              }
175. 
176.            }
177. 
178.        }
179. 
180.    }
181.}


4. 실행결과





5. 피드백


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

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

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

 


[참조]

 -http://www.dofactory.com

 -Head First Design Patterns

And