命令模式

# 分类

行为型模式

# 定义

  • 命令模式(Command Pattern) 是一种数据驱动的设计模式,请求以命令的形式包裹在对象中,并传给调用对象,调用对象寻找可以处理命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

# 意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

# 应用场景

  • 主要解决?在软件系统中行为请求者行为实现者通常是一种紧耦合的关系。但某些场合,比如需要对行为进行记录、撤销、重做以及事务 等处理时,这种无法抵御变化的紧耦合的设计就不太适合。
  • 何时使用?在遇到需要将行为请求者行为实现者解耦的场合时,将一组行为抽象为对象,可以实现二者之间的松耦合。
  • 如何解决?通过调用者调用接收者执行命令,顺序:调用者--->命令--->接受者
  • 关键代码?定义三个角色:1 received 真正的命令执行对象 2 command 3 invoker 使用命令对象的入口。

# 角色与结构图

  • Command:抽象命令类角色 用来声明执行操作的接口。
  • ConcreateCommand:具体命令类角色 将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute。
  • Invoker:调用者类角色 要求该命令执行这个请求。
  • Recevier:接收者类角色 知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。

下图解释了命令模式中各角色的作用

<<------------------------命令模式结构图--------------------------->>

# 示例代码

//羊肉串师傅
public class Barbecuer {
  public void BakeMutton(){
    Console.println("烤羊肉串!");
  }

  public void BakeChickenWing(){
    Console.println("烤鸡翅膀!");
  }
}

//抽象命令
public  abstract class Command{
  protected Barbecuer receiver;

  public Command(Barbecuer receiver){
    this.receiver = receiver;
  }

  abstract public void ExecuteCommand();
}

//烤羊肉串命令
class BakeMuttonCommand : Command{
  public BakeMuttonCommand(Barbecuer receiver):base(receiver){}

  public override void ExecuteCommand(){
    receiver.ExecuteCommand();
  }
}

//烤鸡翅命令
class BakeChickenCommand : Command{
  public BakeChickenCommand(Barbecuer receiver):base(receiver){}

  public override void ExecuteCommand(){
    receiver.ExecuteCommand();
  }
}

//服务员类
public class Waiter{
  private Ilist<Command>  orders = new List<Command>();

  //新增订单
  public void SetOrder(Command command){
    if(command.ToString() == "CommandPattern.BakeChickenWingCommand"){
       Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤");
    }else{
      orders.Add(command);
      Console.WriteLine("增加订单" + command.ToString() + "时间:" + DateTime.Now().ToString());
    }
  }

  //取消订单
  public void CancelOrder(Command command){
    orders.Remove(command);
    Console.WriteLine("取消订单" + command.ToString() + "时间:" + DateTime.Now().ToString());
  }

 //执行全部命令
  public void Notify(){
    foreach(Command order in orders){
      order.ExecuteCommand();
    }
  }
}

//客户端代码
static void Main(string[] args){
  Barbecuer boy = new Barbecuer();

  Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
  Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
  Command bakeChickenCommand1 = new BakeChickenWingCommand(boy);
   
  Waiter girl = new Waiter();

  girl.SetOrder(bakeMuttonCommand1);
  girl.SetOrder(bakeMuttonCommand2);
  girl.SetOrder(bakeChickenCommand1);

  girl.Notify();
  
  Console.ReadLine();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

通用写法

// 抽象命令类
abstract class Command{
  protected Receiver receiver;

  public Command(Receiver receiver){
    this.receiver = receiver;
  }

  abstract public void Execute();
}
// 具体命令类
class ConcreteCommand : Command{
  public ConcreteCommand(Receiver receiver):base(receiver){}

  public override void Execute(){
    receiver.Action();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//要求该命令执行这个请求
class Invoker{
  private Command command;

  public void SetCommand(Command command){
    this.command = command;
  }

  public void ExecuteCommand(){
    command.Execute();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
//知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者
class Receiver{
  public void Action(){
    Console.WriteLine("执行请求");
  }
}
1
2
3
4
5
6
// 客户端
class Client{
    public static void Main(string[] args){
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.SetCommand(command);
        invoker.Execute();
        Console.ReadLine("");
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 优点

  • 请求和执行分离,系统耦合度降低。
  • 扩展容易,新的命令很容易添加到系统中。

# 缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类

# 小结

  • 注意:当系统需要支持命令的撤销操作和恢复操作,也可以考虑使用命令模式。
  • 它能较容易地设计一个命令队列。
  • 在需要的情况下,可以较容易地将命令记入日志。
  • 允许接收请求的一方决定是否要否决请求。
  • 可以容易地实现对请求的撤销和重做。
  • 由于加进新的具体命令类不影响其它的类,因此增加新的具体命令类很容易。
  • 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分离开。

# 一句话概括

将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

上次更新: 2025/03/22, 13:47:44
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×