Command

  • Undo、Redo を実装できる
  • 何かを行うタイミングだけがわかっていて、具体的な処理内容がわからない場合(GUIメニューからのコマンドとか?)に使う
  • クライアントからのコマンド要求をキューに貯めて、順番に実行することもできる
  1. #include <iostream>
  2. #include <list>
  3. using namespace std;
  4.  
  5. // "Receiver" class
  6. // デバイスとか、出力するオブジェクトな感じ
  7. class Display {
  8. public:
  9. void Action(double result, double oprnd_1st, double oprnd_2nd,
  10. string operation)
  11. {
  12. cout << result << " (" << oprnd_1st << " "
  13. << operation << " " << oprnd_2nd << ")" << endl;
  14. }
  15. void UndoAction(double result) {
  16. cout << "[undo] " << result << endl;
  17. }
  18. void RedoAction(double result, double oprnd_1st, double oprnd_2nd,
  19. string operation)
  20. {
  21. cout << "[redo] " << result << " (" << oprnd_1st << " "
  22. << operation << " " << oprnd_2nd << ")" << endl;
  23. }
  24. };
  25.  
  26. // "Command" abstract class
  27. class Command {
  28. public:
  29. Command(Display *disp, double operand) {
  30. m_disp = disp;
  31. m_operand = operand;
  32. }
  33. virtual double Execute(double cur_val) = 0;
  34. virtual double Undo() = 0;
  35. virtual double Redo() = 0;
  36. protected:
  37. Display *m_disp;
  38. double m_org_val, m_operand, m_result;
  39. };
  40.  
  41. // "ConcreteCommand" class
  42. class PlusCommand : public Command {
  43. public:
  44. PlusCommand(Display *disp, double operand)
  45. : Command(disp, operand) { }
  46. double Execute(double cur_val) {
  47. m_org_val = cur_val;
  48. m_result = m_org_val + m_operand;
  49. m_disp->Action(m_result, m_org_val, m_operand, "+");
  50. return m_result;
  51. }
  52. double Undo() {
  53. m_disp->UndoAction(m_org_val);
  54. return m_org_val;
  55. }
  56. double Redo() {
  57. m_disp->RedoAction(m_result, m_org_val, m_operand, "+");
  58. return m_result;
  59. }
  60. };
  61.  
  62. // "ConcreteCommand" class
  63. class MinusCommand : public Command {
  64. public:
  65. MinusCommand(Display *disp, double operand)
  66. : Command(disp, operand) { }
  67. double Execute(double cur_val) {
  68. m_org_val = cur_val;
  69. m_result = m_org_val - m_operand;
  70. m_disp->Action(m_result, m_org_val, m_operand, "-");
  71. return m_result;
  72. }
  73. double Undo() {
  74. m_disp->UndoAction(m_org_val);
  75. return m_org_val;
  76. }
  77. double Redo() {
  78. m_disp->RedoAction(m_result, m_org_val, m_operand, "-");
  79. return m_result;
  80. }
  81. };
  82.  
  83. // "ConcreteCommand" class
  84. class MultiplicationCommand : public Command {
  85. public:
  86. MultiplicationCommand(Display *disp, double operand)
  87. : Command(disp, operand) { }
  88. double Execute(double cur_val) {
  89. m_org_val = cur_val;
  90. m_result = m_org_val * m_operand;
  91. m_disp->Action(m_result, m_org_val, m_operand, "*");
  92. return m_result;
  93. }
  94. double Undo() {
  95. m_disp->UndoAction(m_org_val);
  96. return m_org_val;
  97. }
  98. double Redo() {
  99. m_disp->RedoAction(m_result, m_org_val, m_operand, "*");
  100. return m_result;
  101. }
  102. };
  103.  
  104. // "ConcreteCommand" class
  105. class DivisionCommand : public Command {
  106. public:
  107. DivisionCommand(Display *disp, double operand)
  108. : Command(disp, operand) { }
  109. double Execute(double cur_val) {
  110. if (m_operand == 0) {
  111. cout << "division error." << endl;
  112. return cur_val;
  113. } else {
  114. m_org_val = cur_val;
  115. m_result = m_org_val / m_operand;
  116. m_disp->Action(m_result, m_org_val, m_operand, "/");
  117. return m_result;
  118. }
  119. }
  120. double Undo() {
  121. m_disp->UndoAction(m_org_val);
  122. return m_org_val;
  123. }
  124. double Redo() {
  125. m_disp->RedoAction(m_result, m_org_val, m_operand, "/");
  126. return m_result;
  127. }
  128. };
  129.  
  130. // "Invoker" class
  131. class Calculator {
  132. public:
  133. Calculator(double init_val) {
  134. m_cur_val = init_val;
  135. cout << "initial value: " << m_cur_val << endl;
  136. m_cur_cmd_it = m_history.begin();
  137. }
  138. ~Calculator() {
  139. list<Command*>::iterator it = m_history.begin();
  140. while (it != m_history.end()) {
  141. delete *it;
  142. ++it;
  143. }
  144. }
  145. void Exec(Command *cmd) {
  146. if (m_cur_cmd_it != m_history.begin()) {
  147. // 戻した分を削除
  148. m_history.erase(m_history.begin(), m_cur_cmd_it);
  149. }
  150. m_history.push_front(cmd); // 先頭に追加
  151. m_cur_cmd_it = m_history.begin();
  152. m_cur_val = cmd->Execute(m_cur_val);
  153. }
  154. void Undo(unsigned int num = 1) {
  155. while (num--) {
  156. if (m_cur_cmd_it == m_history.end()) {
  157. cout << "could not undo." << endl;
  158. break;
  159. }
  160. m_cur_val = (*m_cur_cmd_it)->Undo();
  161. ++m_cur_cmd_it;
  162. }
  163. }
  164. void Redo(unsigned int num = 1) {
  165. while (num--) {
  166. if (m_cur_cmd_it == m_history.begin()) {
  167. cout << "could not redo." << endl;
  168. return;
  169. }
  170. --m_cur_cmd_it;
  171. m_cur_val = (*m_cur_cmd_it)->Redo();
  172. }
  173. }
  174. private:
  175. double m_cur_val;
  176. list<Command*> m_history;
  177. list<Command*>::iterator m_cur_cmd_it;
  178. };
  179.  
  180. // client
  181. int main() {
  182. // invoker
  183. Calculator *calc = new Calculator(0);
  184. // receiver
  185. Display *disp = new Display();
  186. calc->Exec(new PlusCommand(disp, 10)); // 10
  187. calc->Exec(new PlusCommand(disp, 10)); // 20
  188. calc->Exec(new MinusCommand(disp, 5)); // 15
  189. calc->Exec(new MultiplicationCommand(disp, 3)); // 45
  190. calc->Exec(new DivisionCommand(disp, 10)); // 4.5
  191. calc->Undo(3); // 45 => 15 => 20
  192. calc->Redo(2); // 15 => 45
  193. calc->Exec(new PlusCommand(disp, 12)); // 57
  194. calc->Exec(new MinusCommand(disp, 2)); // 55
  195. calc->Exec(new DivisionCommand(disp, 10)); // 5.5
  196. calc->Undo(); // 55
  197. calc->Redo(2);
  198.  
  199. delete disp;
  200. delete calc;
  201. return 0;
  202. }
  203.  

出力
initial value: 0
10 (0 + 10)
20 (10 + 10)
15 (20 - 5)
45 (15 * 3)
4.5 (45 / 10)
[undo] 45
[undo] 15
[undo] 20
[redo] 15 (20 - 5)
[redo] 45 (15 * 3)
57 (45 + 12)
55 (57 - 2)
5.5 (55 / 10)
[undo] 55
[redo] 5.5 (55 / 10)
could not redo.


参考サイト

デザインパターンを“喩え話”で分かり易く理解する
http://www.netlaputa.ne.jp/~hijk/study/oo/designpattern.html



デザインパターンの骸骨たち
http://www002.upp.so-net.ne.jp/ys_oota/mdp/

デザインパターンの使い方: Command
http://japan.internet.com/developer/20081003/26.html
最終更新:2012年02月07日 03:41