Command
命令
Commandパターンは、「命令」をオブジェクトとして扱うパターンです。これにより「命令」を呼び出す側は、その「命令」が実際どのように動作するか気にする必要がなくなります。
このパターンの概要は以下の通り。
- 「命令」を実行するメソッドExecuteをもつインターフェイスを定義します。
C#
public interface ICommand
{
void Execute();
}
VB
Public Interface ICommand
Sub Execute()
End Interface
- 「命令」を受け取るクラス、Recieverを定義します。RecieverはどんなクラスでもOKです。コード例ではLightにしました。
C#
public class Light
{
private enum State
{
On
, Off
}
private State state = State.Off;
public void On()
{
this.state = State.On;
Console.WriteLine("ライトが点きました");
}
public void Off()
{
this.state = State.Off;
Console.WriteLine("ライトが消えました");
}
public void DisplayState()
{
if (this.state == State.On)
{
Console.WriteLine("ライトは点いています");
}
else
{
Console.WriteLine("ライトは消えています");
}
}
}
VB
Public Class Light
Private Enum PowerState
[On]
Off
End Enum
Private state As PowerState = PowerState.Off
Public Sub [On]()
Me.state = PowerState.On
Console.WriteLine("ライトが点きました")
End Sub
Public Sub Off()
Me.state = PowerState.Off
Console.WriteLine("ライトが消えました")
End Sub
Public Sub DisplayState()
If Me.state = PowerState.On Then
Console.WriteLine("ライトは点いています")
Else
Console.WriteLine("ライトは消えています")
End If
End Sub
End Class
- 実際にRecieverに対しての命令をICommandを実装したクラスとして定義します。コード例ではLightのOn、OffそれぞれのCommandを定義しています。
C#
public class LightOnCommand : ICommand
{
private Light light;
public LightOnCommand(Light light)
{
this.light = light;
}
public void Execute()
{
this.light.On();
}
}
public class LightOffCommand : ICommand
{
private Light light;
public LightOffCommand(Light light)
{
this.light = light;
}
public void Execute()
{
this.light.Off();
}
}
VB
Public Class LightOnCommand
Implements ICommand
Private light As Light
Public Sub New(ByVal light As Light)
Me.light = light
End Sub
Public Sub Execute() Implements ICommand.Execute
Me.light.On()
End Sub
End Class
Public Class LightOffCommand
Implements ICommand
Private light As Light
Public Sub New(ByVal light As Light)
Me.light = light
End Sub
Public Sub Execute() Implements ICommand.Execute
Me.light.Off()
End Sub
End Class
- Commandを登録して、実際に起動するInvokerを定義します。コード例ではRemoteControllerとして、On、OffそれぞれのCommandを登録できるようにしています。
C#
public class RemoteController
{
private ICommand onCommand;
public ICommand OnCommand
{
set
{
this.onCommand = value;
}
}
private ICommand offCommand;
public ICommand OffCommand
{
set
{
this.offCommand = value;
}
}
public void OnButtonWasPressed()
{
if (this.onCommand == null)
{
return;
}
this.onCommand.Execute();
}
public void OffButtonWasPressed()
{
if (this.offCommand == null)
{
return;
}
this.offCommand.Execute();
}
}
VB
Public Class RemoteController
Private _onCommand As ICommand
Public WriteOnly Property OnCommand() As ICommand
Set(ByVal value As ICommand)
Me._onCommand = value
End Set
End Property
Private _offCommand As ICommand
Public WriteOnly Property OffCommand() As ICommand
Set(ByVal value As ICommand)
_offCommand = value
End Set
End Property
Public Sub OnButtonWasPressed()
If Me._onCommand Is Nothing Then
Return
End If
Me._onCommand.Execute()
End Sub
Public Sub OffButtonWasPressed()
If Me._offCommand Is Nothing Then
Return
End If
Me._offCommand.Execute()
End Sub
End Class
実行してみましょう。Clientでは次のようにCommandを使用します。
C#
public class Program
{
public static void Main(string[] args)
{
// Invokerのインスタンス作成
RemoteController remoCon = new RemoteController();
Console.WriteLine("■Lightに対してCommand実行");
// Reciever:Lightのインスタンス作成
Light light = new Light();
light.DisplayState();
// Light用Command作成
LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);
// Light用CommandをInvokerに設定
remoCon.OnCommand = lightOnCommand;
remoCon.OffCommand = lightOffCommand;
// InvokerよりOnCommand実行
remoCon.OnButtonWasPressed();
light.DisplayState();
// InvokerよりOffCommand実行
remoCon.OffButtonWasPressed();
light.DisplayState();
Console.ReadKey();
}
}
VB
Public Class Program
Public Shared Sub Main(ByVal args As String())
' Invokerのインスタンス作成
Dim remoCon As New RemoteController()
Console.WriteLine("■Lightに対してCommand実行")
' Reciever:Lightのインスタンス作成
Dim light As New Light()
light.DisplayState()
' Light用Command作成
Dim lightOnCommand As New LightOnCommand(light)
Dim lightOffCommand As New LightOffCommand(light)
' Light用CommandをInvokerに設定
remoCon.OnCommand = lightOnCommand
remoCon.OffCommand = lightOffCommand
' InvokerよりOnCommand実行
remoCon.OnButtonWasPressed()
light.DisplayState()
' InvokerよりOffCommand実行
remoCon.OffButtonWasPressed()
light.DisplayState()
Console.ReadKey()
End Sub
End Class
上記のクラス、インターフェイスの関連をクラス図で現わすと、次のようになります。
それでは、コードを実行してみましょう。
ClientはInvokerに登録されたCommandを通して、Recieverの動作を呼び出していることがわかります。
そして、Commandを入れ替えれば、他のRecieverに対しても処理を行うことが可能です。
例えばCDPlayerに対する処理を行うとして、次のようにReciever、Commandを新たに作成し、Clientのコードを変更します。このとき、Invoker、すでに作成されたCommandへの変更を行う必要はありません。
C#
public class CDPlayer
{
enum State
{
Play
, Stop
}
private State state = State.Stop;
public void Play()
{
this.state = State.Play;
Console.WriteLine("CDが再生されました");
}
public void Stop()
{
this.state = State.Stop;
Console.WriteLine("CDが停止されました");
}
public void DisplayState()
{
if (this.state == State.Play)
{
Console.WriteLine("CDは再生中です");
}
else
{
Console.WriteLine("CDは停止中です");
}
}
}
public class CDPlayerPlayCommand : ICommand
{
private CDPlayer player;
public CDPlayerPlayCommand(CDPlayer player)
{
this.player = player;
}
#region ICommand メンバ
public void Execute()
{
this.player.Play();
}
#endregion
}
public class CDPlayerStopCommand : ICommand
{
private CDPlayer player;
public CDPlayerStopCommand(CDPlayer player)
{
this.player = player;
}
#region ICommand メンバ
public void Execute()
{
this.player.Stop();
}
#endregion
}
public class Program
{
public static void Main(string[] args)
{
// Invokerのインスタンス作成
RemoteController remoCon = new RemoteController();
Console.WriteLine("■CDPlayerに対してCommand実行");
// Reciever:CDPlayerのインスタンス作成
CDPlayer player = new CDPlayer();
player.DisplayState();
// CDPlayer用Command作成
CDPlayerPlayCommand playCommand = new CDPlayerPlayCommand(player);
CDPlayerStopCommand stopCommand = new CDPlayerStopCommand(player);
// CDPlaer用CommandをInvokerに設定
remoCon.OnCommand = playCommand;
remoCon.OffCommand = stopCommand;
// InvokerよりOnCommand実行
remoCon.OnButtonWasPressed();
player.DisplayState();
// InvokerよりOffCommand実行
remoCon.OffButtonWasPressed();
player.DisplayState();
Console.ReadKey();
}
}
VB
Public Class CDPlayer
Private Enum BehaviorState
Play
[Stop]
End Enum
Private state As BehaviorState = BehaviorState.[Stop]
Public Sub Play()
Me.state = BehaviorState.Play
Console.WriteLine("CDが再生されました")
End Sub
Public Sub [Stop]()
Me.state = BehaviorState.[Stop]
Console.WriteLine("CDが停止されました")
End Sub
Public Sub DisplayState()
If Me.state = BehaviorState.Play Then
Console.WriteLine("CDは再生中です")
Else
Console.WriteLine("CDは停止中です")
End If
End Sub
End Class
Public Class CDPlayerPlayCommand
Implements ICommand
Private player As CDPlayer
Public Sub New(ByVal player As CDPlayer)
Me.player = player
End Sub
Public Sub Execute() Implements ICommand.Execute
Me.player.Play()
End Sub
End Class
Public Class CDPlayerStopCommand
Implements ICommand
Private player As CDPlayer
Public Sub New(ByVal player As CDPlayer)
Me.player = player
End Sub
Public Sub Execute() Implements ICommand.Execute
Me.player.Stop()
End Sub
End Class
Public Class Program
Public Shared Sub Main(ByVal args As String())
' Invokerのインスタンス作成
Dim remoCon As New RemoteController()
Console.WriteLine("■CDPlayerに対してCommand実行")
' Reciever:CDPlayerのインスタンス作成
Dim player As New CDPlayer()
player.DisplayState()
' CDPlayer用Command作成
Dim playCommand As New CDPlayerPlayCommand(player)
Dim stopCommand As New CDPlayerStopCommand(player)
' Light用CommandをInvokerに設定
remoCon.OnCommand = playCommand
remoCon.OffCommand = stopCommand
' InvokerよりOnCommand実行
remoCon.OnButtonWasPressed()
player.DisplayState()
' InvokerよりOffCommand実行
remoCon.OffButtonWasPressed()
player.DisplayState()
Console.ReadKey()
End Sub
End Class
CDPlayerの動作に変更されたことが確認できました。
さて、以上がCommandパターンの基本形です。
次回は、複数のCommandをまとめて呼び出す方法について紹介します。