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
namespace csConsole { interface ICommand { void Execute(); void Undo(); } }
Light.cs : コンクリートとレシーバーその1
namespace csConsole { class Light // レシーバー部分 { public Light(){} public void on() => Console.WriteLine("ライトオン です。"); public void off() => Console.WriteLine("ライトOFF です。"); } class LightOnCommand : ICommand // コンクリート部分 { Light light; public LightOnCommand(Light light) => this.light = light; public void Execute() => light.on(); public void Undo() => light.off(); } class LightOffCommand : ICommand // コンクリート部分 { Light light; public LightOffCommand(Light light) => this.light = light; public void Execute() => light.off(); public void Undo() => light.on(); } }
CeilingFan : コンクリートとレシーバーその2
{ class CeilingFan { public const int HIGH = 3; public const int MEDIUM = 2; public const int OFF = 0; int speed; public CeilingFan() => speed = OFF; public void high() => speed = HIGH; public void medium() => speed = MEDIUM; public int getSpeed() => speed; } class CeilingFanHighCommand : ICommand { CeilingFan CeilingFan; int prevSpeed; public CeilingFanHighCommand(CeilingFan ceilingFan) => CeilingFan = ceilingFan; public void Execute() { this.prevSpeed = CeilingFan.getSpeed(); CeilingFan.high(); } public void Undo() { switch (prevSpeed) { case CeilingFan.HIGH: CeilingFan.high(); break; case CeilingFan.MEDIUM: CeilingFan.medium(); break; } } } class CeilingFanMediumCommand : ICommand { CeilingFan CeilingFan; int prevSpeed; public CeilingFanMediumCommand(CeilingFan ceilingFan) => CeilingFan = ceilingFan; public void Execute() { this.prevSpeed = CeilingFan.getSpeed(); CeilingFan.medium(); } public void Undo() { switch (prevSpeed) { case CeilingFan.HIGH: CeilingFan.high(); break; case CeilingFan.MEDIUM: CeilingFan.medium(); break; } } } }
NoCommand.cs : コンクリートとレシーバーその3
namespace csConsole { class NoCommand : ICommand { public void Execute() => Console.WriteLine("NoCommand"); public void Undo() => Console.WriteLine("NoCommand : Undo"); } }
invoker : 記事的にはリモコンクラス
namespace csConsole { class RemoteControl { ICommand[] onCommands; ICommand[] offCommands; Stack < ICommand > undoCmds; //「<」の前後に半角スペース入れてます。ブログ化したら消えたので。 // コンストラクタ public RemoteControl() { // 例題通り7個のボタン this.onCommands = new ICommand[7]; this.offCommands = new ICommand[7]; undoCmds = new Stack(); // 初期化 ICommand noCommand = new NoCommand(); for (int i = 0 ; i < 7; i++) { this.onCommands[i]=noCommand; this.offCommands[i]=noCommand; } } } public void setCommand( int slot, ICommand onCmd, ICommand offCmd ) { this.onCommands[slot]=onCmd; this.offCommands[slot]=offCmd; } public void onPush( int slot ) { this.onCommands[slot].Execute(); this.undoCmds.Push(this.onCommands[slot]); } public void offPush(int slot) { this.offCommands[slot].Execute(); this.undoCmds.Push(this.offCommands[slot]); } public void UndoPush() { this.undoCmds.Pop().Undo(); } }
client : 記事的にはリモートローダー(リモコン定義)
namespace csConsole { class RemoteLoader { static public void setRemoteControler(RemoteControl remote) { // コンクリートコマンドの作成 // Light Light light = new Light(); LightOnCommand lightOnCommand = new LightOnCommand(light); LightOffCommand lightOffCommand = new LightOffCommand(light); // CeilingFan CeilingFan ceilingFan = new CeilingFan(); CeilingFanHighCommand ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan); CeilingFanMediumCommand ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan); // コマンドをリモコンに設定 remote.setCommand(0, lightOnCommand, lightOffCommand); remote.setCommand(1, ceilingFanHighCommand, ceilingFanMediumCommand); } } }
プログラムのMainクラス
namespace csConsole { class Program { static void Main(string[] args) { // リモコン生成 RemoteControl remote = new RemoteControl(); // リモコンの内容設定 RemoteLoader.setRemoteControler(remote); remote.onPush(0); // ここはLightオン。 remote.UndoPush(); // LightオンのUndo = オフ。 remote.onPush(1); //HIGH remote.offPush(1); //MEDIUM remote.UndoPush(); //MEDIUMオブジェクトの中のUNDOでHIGHを呼ぶ。 Console.WriteLine("リモコン終了"); } } }
所感
コードの構造はざっくり把握できたかと。Undo処理については「Undoとはなにか」という哲学になりそうな感じ。
とりあえずFILOを簡単に使いたかったからStackを選定したけども、
これだと無限にStackするし、実用のときはなんらか工夫が必要ですね。
ところでGoFの本も結城本も、クラス図でClientクラスからInvokerクラスへの関連線が出てないのって
どうしてなんだろうか。
インスタンス持ちますよねぇ。
コメント
コメントを投稿