HeadFirstデザインパターン 第6章CommandパターンをC#で軽く実装。

自分のお勉強用 : HeadFirstデザインパターン 第6章CommandパターンをC#で軽く実装。


C#のおさらいとCommandパターンの感触を掴むため、
書籍「HeadFirstデザインパターン」の第6章Commandパターンを適当に実装。

ファイル一覧

  • CeilingFan.cs
  • ICommand.cs
  • Light.cs
  • NoCommand.cs
  • Program.cs
  • RemoteControl.cs
  • RemoteLoader.cs

実装

前述のファイル一覧の順番とは異なります。
まずはインターフェイスから。

ICommand.cs

  1. namespace csConsole
  2. {
  3. interface ICommand
  4. {
  5. void Execute();
  6. void Undo();
  7. }
  8. }

Light.cs : コンクリートとレシーバーその1

  1. namespace csConsole
  2. {
  3. class Light // レシーバー部分
  4. {
  5. public Light(){}
  6. public void on() => Console.WriteLine("ライトオン です。");
  7. public void off() => Console.WriteLine("ライトOFF です。");
  8. }
  9. class LightOnCommand : ICommand // コンクリート部分
  10. {
  11. Light light;
  12. public LightOnCommand(Light light) => this.light = light;
  13. public void Execute() => light.on();
  14. public void Undo() => light.off();
  15. }
  16. class LightOffCommand : ICommand // コンクリート部分
  17. {
  18. Light light;
  19. public LightOffCommand(Light light) => this.light = light;
  20. public void Execute() => light.off();
  21. public void Undo() => light.on();
  22. }
  23. }

CeilingFan : コンクリートとレシーバーその2

  1. {
  2. class CeilingFan
  3. {
  4. public const int HIGH = 3;
  5. public const int MEDIUM = 2;
  6. public const int OFF = 0;
  7. int speed;
  8.  
  9. public CeilingFan() => speed = OFF;
  10. public void high() => speed = HIGH;
  11. public void medium() => speed = MEDIUM;
  12. public int getSpeed() => speed;
  13. }
  14. class CeilingFanHighCommand : ICommand
  15. {
  16. CeilingFan CeilingFan;
  17. int prevSpeed;
  18.  
  19. public CeilingFanHighCommand(CeilingFan ceilingFan) => CeilingFan = ceilingFan;
  20.  
  21. public void Execute()
  22. {
  23. this.prevSpeed = CeilingFan.getSpeed();
  24. CeilingFan.high();
  25. }
  26.  
  27. public void Undo()
  28. {
  29. switch (prevSpeed)
  30. {
  31. case CeilingFan.HIGH:
  32. CeilingFan.high();
  33. break;
  34. case CeilingFan.MEDIUM:
  35. CeilingFan.medium();
  36. break;
  37. }
  38. }
  39. }
  40.  
  41. class CeilingFanMediumCommand : ICommand
  42. {
  43. CeilingFan CeilingFan;
  44. int prevSpeed;
  45.  
  46. public CeilingFanMediumCommand(CeilingFan ceilingFan) => CeilingFan = ceilingFan;
  47.  
  48. public void Execute()
  49. {
  50. this.prevSpeed = CeilingFan.getSpeed();
  51. CeilingFan.medium();
  52. }
  53.  
  54. public void Undo()
  55. {
  56. switch (prevSpeed)
  57. {
  58. case CeilingFan.HIGH:
  59. CeilingFan.high();
  60. break;
  61. case CeilingFan.MEDIUM:
  62. CeilingFan.medium();
  63. break;
  64. }
  65. }
  66. }
  67. }

NoCommand.cs : コンクリートとレシーバーその3

  1. namespace csConsole
  2. {
  3. class NoCommand : ICommand
  4. {
  5. public void Execute() => Console.WriteLine("NoCommand");
  6. public void Undo() => Console.WriteLine("NoCommand : Undo");
  7. }
  8. }

invoker : 記事的にはリモコンクラス

  1. namespace csConsole
  2. {
  3. class RemoteControl
  4. {
  5. ICommand[] onCommands;
  6. ICommand[] offCommands;
  7. Stack < ICommand > undoCmds; //「<」の前後に半角スペース入れてます。ブログ化したら消えたので。
  8.  
  9. // コンストラクタ
  10. public RemoteControl()
  11. {
  12. // 例題通り7個のボタン
  13. this.onCommands = new ICommand[7];
  14. this.offCommands = new ICommand[7];
  15. undoCmds = new Stack();
  16. // 初期化
  17. ICommand noCommand = new NoCommand();
  18. for (int i = 0 ; i < 7; i++)
  19. {
  20. this.onCommands[i]=noCommand;
  21. this.offCommands[i]=noCommand;
  22. }
  23. }
  24. }
  25. public void setCommand( int slot, ICommand onCmd, ICommand offCmd )
  26. {
  27. this.onCommands[slot]=onCmd;
  28. this.offCommands[slot]=offCmd;
  29. }
  30. public void onPush( int slot )
  31. {
  32. this.onCommands[slot].Execute();
  33. this.undoCmds.Push(this.onCommands[slot]);
  34. }
  35. public void offPush(int slot)
  36. {
  37. this.offCommands[slot].Execute();
  38. this.undoCmds.Push(this.offCommands[slot]);
  39. }
  40. public void UndoPush()
  41. {
  42. this.undoCmds.Pop().Undo();
  43. }
  44. }

client : 記事的にはリモートローダー(リモコン定義)

  1. namespace csConsole
  2. {
  3. class RemoteLoader
  4. {
  5. static public void setRemoteControler(RemoteControl remote)
  6. {
  7. // コンクリートコマンドの作成
  8. // Light
  9. Light light = new Light();
  10. LightOnCommand lightOnCommand = new LightOnCommand(light);
  11. LightOffCommand lightOffCommand = new LightOffCommand(light);
  12.  
  13. // CeilingFan
  14. CeilingFan ceilingFan = new CeilingFan();
  15. CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
  16. CeilingFanMediumCommand ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan);
  17.  
  18. // コマンドをリモコンに設定
  19. remote.setCommand(0, lightOnCommand, lightOffCommand);
  20. remote.setCommand(1, ceilingFanHighCommand, ceilingFanMediumCommand);
  21. }
  22. }
  23. }

プログラムのMainクラス

  1. namespace csConsole
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. // リモコン生成
  8. RemoteControl remote = new RemoteControl();
  9.  
  10. // リモコンの内容設定
  11. RemoteLoader.setRemoteControler(remote);
  12.  
  13. remote.onPush(0); // ここはLightオン。
  14. remote.UndoPush(); // LightオンのUndo = オフ。
  15.  
  16. remote.onPush(1); //HIGH
  17. remote.offPush(1); //MEDIUM
  18. remote.UndoPush(); //MEDIUMオブジェクトの中のUNDOでHIGHを呼ぶ。
  19.  
  20. Console.WriteLine("リモコン終了");
  21. }
  22. }
  23. }

所感

コードの構造はざっくり把握できたかと。
Undo処理については「Undoとはなにか」という哲学になりそうな感じ。
とりあえずFILOを簡単に使いたかったからStackを選定したけども、
これだと無限にStackするし、実用のときはなんらか工夫が必要ですね。

ところでGoFの本も結城本も、クラス図でClientクラスからInvokerクラスへの関連線が出てないのって
どうしてなんだろうか。
インスタンス持ちますよねぇ。

コメント

このブログの人気の投稿

windows10 で nvidia のグラボのcode43現象を解決した

Java : processbuilder 標準出力 タイムアウト

GTX560Ti がおかしい(code 43が出る)(2018年)→解決しました(2019)