设计模式-命令模式
定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
使用场景
(1) 需要对方法的请求这和方法的实现者解耦,使得调用者和接收者不直接交互
(2) 需要对操作进行 撤销、恢复、记录
(3) 需要在不同的时间指定请求、将请求排队和执行请求
命令模式类图如下
Receiver 接收者角色
可以是具体类,也可以是抽象类、接口,执行命令的角色
Command 抽象命令角色
抽象出命令对象,可以根据不同的命令类型,写出不同的实现类
ConcreteCommand 具体命令类
实现了抽象命令类的角色
Invoker 调用者/请求者
请求的发送者,它通过命令对象来执行请求,可以持有若干个命令对象
Client 客户端
在客户端(不是常规意义上的客户端,而是组装命令对象和接受者的类)中需要创建调用者对象,具体命令类对象,在创建具体命令对象时指定对应的接收者,发送者和接收者之间没有关联,可以通过命令对象来调用。
实例:
应该都玩过游戏,我们通过操作游戏界面上的按钮,来控制游戏人物 移动、跳跃、攻击等,玩家每次操作的按钮可以封装为不同的具体命令,游戏人物则是这些命令的接收者。
代码实现如下
Receiver 接收者角色
下面直接定义了具体的接收者,也可以对接收者加一层抽象
// 游戏人物:接收者
public class Player
{
// 移动
public void Move(int x, int y)
{
string msg = string.Format("向着摇杆_({0}, {1}) 方向移动 \n", x, y);
Console.WriteLine(msg);
}
// 跳跃
public void Jump()
{
Console.WriteLine("跳起 \n");
}
// 攻击
public void Attack()
{
Console.WriteLine("攻击 \n");
}
}
Command 抽象命令角色
// 抽象命令
public interface ICommand
{
void OnExecute();
}
ConcreteCommand 具体命令类
// 摇杆命令:具体命令
public class RockerCommand : ICommand
{
private Player _player;
private int _x;
private int _y;
public RockerCommand(Player player)
{
_player = player;
}
public void RockerInfo(int x, int y)
{
_x = x;
_y = y;
}
public void OnExecute()
{
// 调用移动
_player.Move(_x, _y);
}
}
// 起跳命令:具体命令
public class JumpCommand : ICommand
{
private Player _player;
public JumpCommand(Player player)
{
_player = player;
}
public void OnExecute()
{
// 调用跳跃
_player.Jump();
}
}
// 攻击命令:具体命令
public class AttackCommand : ICommand
{
private Player _player;
public AttackCommand(Player player)
{
_player = player;
}
public void OnExecute()
{
// 调用攻击
_player.Attack();
}
}
Invoker 调用者/请求者
class Game
{
private RockerCommand _rockerCommand;
private JumpCommand _jumpCommand;
private AttackCommand _attackCommand;
public Game()
{
}
public void SetRockerCommand(RockerCommand command)
{
_rockerCommand = command;
}
public void SetJumpCommand(JumpCommand command)
{
_jumpCommand = command;
}
public void SetAttackCommand(AttackCommand command)
{
_attackCommand = command;
}
public void RockerInfo(int x, int y)
{
_rockerCommand.RockerInfo(5, 6);
// 调用命令
_rockerCommand.OnExecute();
}
public void Jump()
{
// 调用命令
_jumpCommand.OnExecute();
}
public void Attack()
{
// 调用命令
_attackCommand.OnExecute();
}
}
Client 调用如下
public class Client
{
public Client()
{
// 创建接收者
Player player = new Player();
// 创建命令
RockerCommand rockerCommand = new RockerCommand(player);
// 创建命令
JumpCommand jumpCommand = new JumpCommand(player);
// 创建命令
AttackCommand attackCommand = new AttackCommand(player);
// 创建调用者
Game game = new Game();
// 添加调用者与命令对象的引用
game.SetRockerCommand(rockerCommand);
// 添加调用者与命令对象的引用
game.SetJumpCommand(jumpCommand);
// 添加调用者与命令对象的引用
game.SetAttackCommand(attackCommand);
game.RockerInfo(5, 6);
game.Jump();
game.Attack();
}
}
测试结果如下
优点:
(1) 类间解耦,降低了系统耦合度,调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用 Command 抽象类的 execute 方法即可,不需要了解到底是哪个接收者执行。
(2) 可扩展性, Command 的子类可以非常容易地扩展,而调用者 Invoker 和高层次的模块 Client 不产生严重的代码耦合
缺点:
(1) 使用命令模式可能导致某些系统有过多的具体命名类。因为每一个命令都需要设计一个具体命令类。