|
Design Pattern - Command
|
| (史帝芬, 2007/04/28, hi.steven@gmail.com) |
現在我們要設計一個遙控汽車的遙控器,這個遙控器上有八個按鈕, 初期我們只會有四個按鍵是有功能的,其餘四個保留做為未來擴充之用,同時我們希望按鍵上的功 能不是直接設計在遙控器上,這個遙控器現在是用來遙控小汽車,但是要將它改成 電視或其它東西的遙控器,只要裝上不同的按鍵即可。也就是說,遙控器只用來發出命令給實際 執行命令的東西,並不需要知道命令的執行細節。
using System;
namespace Command
{
/// <summary>
/// Receiver: 接收者實際進行必要的工作。
/// 這輛遙控汽車有PowerOn、PowerOff、Move(前進)、Stop(煞車)四項功能。
/// </summary>
public class Car
{
public void PowerOn()
{
Console.WriteLine("Power On");
}
public void PowerOff()
{
Console.WriteLine("Power Off");
}
public void Move()
{
Console.WriteLine("Move");
}
public void Stop()
{
Console.WriteLine("Stop");
}
}
/// <summary>
/// 所有命令都要實現的介面
/// </summary>
public interface Command
{
void execute();
}
#region Concrete Command (實際的命令)
public class PowerOnCommand : Command
{
private Car car;
public PowerOnCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.PowerOn();
}
}
public class PowerOffCommand : Command
{
private Car car;
public PowerOffCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.PowerOff();
}
}
public class MoveCommand : Command
{
private Car car;
public MoveCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.Move();
}
}
public class StopCommand : Command
{
private Car car;
public StopCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.Stop();
}
}
public class NoCommand : Command
{
public void execute() { }
}
#endregion
/// <summary>
/// Invoker: 啟動者
/// </summary>
public class RemoteControl
{
private Command[] commands;
public RemoteControl()
{
commands = new Command[8];
for (int i = 0; i < 8; i++)
{
commands[i] = new NoCommand();
}
}
public void setCommand(int slot, Command cmd)
{
commands[slot] = cmd;
}
public void PushButton(int slot)
{
commands[slot].execute();
}
}
/// <summary>
/// Client: 負責建立ConcreteCommand,並設定其接收者。
/// </summary>
class Program
{
static void Main(string[] args)
{
RemoteControl remoteCtrl = new RemoteControl();
Car car = new Car();
PowerOnCommand powerOn = new PowerOnCommand(car);
PowerOffCommand powerOff = new PowerOffCommand(car);
MoveCommand move = new MoveCommand(car);
StopCommand stop = new StopCommand(car);
remoteCtrl.setCommand(0, powerOn);
remoteCtrl.setCommand(1, powerOff);
remoteCtrl.setCommand(2, move);
remoteCtrl.setCommand(3, stop);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(2);
remoteCtrl.PushButton(3);
remoteCtrl.PushButton(1);
Console.ReadLine();
}
}
}
Power On Move Stop Power Off 上面的RemoteControl class完全不知道Command的實作細節, 只知道按下指定的按鍵,至於要發出什麼命令,由Concrete Command去做就可以了, 而Concrete Command雖然會發出指定的命令,對於如何執行也交給Car去做,透過這樣 的設計RemoteControl (Invoker)及Concrete Command看似很笨,卻比較有擴充彈性。 當我們的汽車又增加了「左轉」、「右轉」功能時,只要再加上兩個Concrete Command, 並設定給RemoteControl,並不需要修改既有程式碼 (程式2)。 程式1中還有一項需特別注意的是,我們還設計了NoCommand, 先將八個按鍵都設定沒有作用,如果遙控器某個沒有功能的按鍵被按下,就會呼叫 NoCommand裡的execute,當然啦~ 這時候什麼事也不會發生。
using System;
namespace Command
{
/// <summary>
/// Receiver: 接收者實際進行必要的工作。
/// 這輛遙控汽車有PowerOn、PowerOff、Move(前進)、Stop(煞車)、TurnLeft(左轉)、TurnRight(右轉)等六項功能。
/// </summary>
public class Car
{
public void PowerOn()
{
Console.WriteLine("Power On");
}
public void PowerOff()
{
Console.WriteLine("Power Off");
}
public void Move()
{
Console.WriteLine("Move");
}
public void Stop()
{
Console.WriteLine("Stop");
}
#region New Command
public void TurnLeft()
{
Console.WriteLine("Turn Left");
}
public void TurnRight()
{
Console.WriteLine("Turn Right");
}
#endregion
}
/// <summary>
/// 所有命令都要實現的介面
/// </summary>
public interface Command
{
void execute();
}
#region Concrete Command (實際的命令)
public class PowerOnCommand : Command
{
private Car car;
public PowerOnCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.PowerOn();
}
}
public class PowerOffCommand : Command
{
private Car car;
public PowerOffCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.PowerOff();
}
}
public class MoveCommand : Command
{
private Car car;
public MoveCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.Move();
}
}
public class StopCommand : Command
{
private Car car;
public StopCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.Stop();
}
}
public class NoCommand : Command
{
public void execute() { }
}
#region New Command
public class TurnLeftCommand : Command
{
private Car car;
public TurnLeftCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.TurnLeft();
}
}
public class TurnRightCommand : Command
{
private Car car;
public TurnRightCommand(Car car)
{
this.car = car;
}
public void execute()
{
car.TurnRight();
}
}
#endregion
#endregion
/// <summary>
/// Invoker: 啟動者
/// </summary>
public class RemoteControl
{
private Command[] commands;
public RemoteControl()
{
commands = new Command[8];
for (int i = 0; i < 8; i++)
{
commands[i] = new NoCommand();
}
}
public void setCommand(int slot, Command cmd)
{
commands[slot] = cmd;
}
public void PushButton(int slot)
{
commands[slot].execute();
}
}
/// <summary>
/// Client: 負責建立ConcreteCommand,並設定其接收者。
/// </summary>
class Program
{
static void Main(string[] args)
{
RemoteControl remoteCtrl = new RemoteControl();
Car car = new Car();
PowerOnCommand powerOn = new PowerOnCommand(car);
PowerOffCommand powerOff = new PowerOffCommand(car);
MoveCommand move = new MoveCommand(car);
StopCommand stop = new StopCommand(car);
remoteCtrl.setCommand(0, powerOn);
remoteCtrl.setCommand(1, powerOff);
remoteCtrl.setCommand(2, move);
remoteCtrl.setCommand(3, stop);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(2);
remoteCtrl.PushButton(3);
remoteCtrl.PushButton(1);
#region New Command
TurnLeftCommand turnLeft = new TurnLeftCommand(car);
TurnRightCommand turnRight = new TurnRightCommand(car);
remoteCtrl.setCommand(4, turnLeft);
remoteCtrl.setCommand(5, turnRight);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(4);
remoteCtrl.PushButton(5);
remoteCtrl.PushButton(1);
#endregion
Console.ReadLine();
}
}
}
Power On Move Stop Power Off Power On Turn Left Turn Right Power Off 為了增加玩家的樂趣,小汽車現在將加入了蛇行的功能,但是我們分析 後發現,蛇行不需真的加入新功能,只需要利用既有功能加以組合就可以蛇行了,這時,我們可以 寫一個組合多項功能的MacroCommand,並修改測試程式如下。
#region Macro Command
public class MacroCommand : Command
{
private Command[] command;
public MacroCommand(Command[] command)
{
this.command = command;
}
public void execute()
{
for(int i=0; i < command.Length; i++)
{
command[i].execute();
}
}
}
#endregion
class Program
{
static void Main(string[] args)
{
RemoteControl remoteCtrl = new RemoteControl();
Car car = new Car();
PowerOnCommand powerOn = new PowerOnCommand(car);
PowerOffCommand powerOff = new PowerOffCommand(car);
MoveCommand move = new MoveCommand(car);
StopCommand stop = new StopCommand(car);
remoteCtrl.setCommand(0, powerOn);
remoteCtrl.setCommand(1, powerOff);
remoteCtrl.setCommand(2, move);
remoteCtrl.setCommand(3, stop);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(2);
remoteCtrl.PushButton(3);
remoteCtrl.PushButton(1);
#region New Command
TurnLeftCommand turnLeft = new TurnLeftCommand(car);
TurnRightCommand turnRight = new TurnRightCommand(car);
remoteCtrl.setCommand(4, turnLeft);
remoteCtrl.setCommand(5, turnRight);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(4);
remoteCtrl.PushButton(5);
remoteCtrl.PushButton(1);
#endregion
#region Macro Command
Command[] cmdSnakeMove = { turnLeft, turnRight, turnRight, turnLeft, move };
MacroCommand macroCmd = new MacroCommand(cmdSnakeMove);
remoteCtrl.setCommand(6, macroCmd);
remoteCtrl.PushButton(0);
remoteCtrl.PushButton(6);
remoteCtrl.PushButton(3);
remoteCtrl.PushButton(1);
#endregion
Console.ReadLine();
}
}
Power On Move Stop Power Off Power On Turn Left Turn Right Power Off Power On Turn Left Turn Right Turn Right Turn Left Move Stop Power Off 設計組合命令時,也可以新增一個新的ConcreteCommand, 由這個新的ConcreteCommand的execute去呼叫多個car的命令,但是,這樣的設計會比 較沒有彈性,每次只要新增一個組合命令時,就要多一個ConcreteCommand,我們這裡 採用動態傳入的方式是為了增加彈性。 |