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クラスへの関連線が出てないのって
どうしてなんだろうか。
インスタンス持ちますよねぇ。
コメント
コメントを投稿