温故知新(10)——命令模式

概述

虽然并不完全符合,命令模式给我的感觉有点像数据库中的中间表。中间表将两个实体表的主键作为外键,将两个实体表关联成多对多的关系。同样命令模式中命令对象就好像中间表一样,将动作的引发者和动作的执行者关联起来,引发者无需知道执行者,执行者也无需了解是谁引发了这个动作。如果仔细想想,动作的引发者和动作的执行者之间也是多对多的关系。下面是GOF给出的意图:

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

这局意图的前半句,个人感觉有些描述得过于抽象,这里仅供参考;后半句怎说明了命令模式的的一些应用场景和用途:

1、可以在对象关系上解耦动作的引发者和动作的执行者;

2、可以在时间上解耦动作的引发和动作的执行;

3、可以实现动作的“撤销”和“重做”,也可以实现“修改日志”,当系统崩溃时,这些修改可以被重做一遍。

4、将一组动作实现为一个事物。

实现命令模式时需要考虑的一个问题是,命令对象的“轻”或“重”,“轻”模式时,命令对象只负责将动作的引发者和动作的执行者关联起来;而“重”模式时,命令对象自身会包含处理逻辑。个人认为通常应该将命令对象实现为“轻”模式,而在一些无需动作的执行者的特殊情况下将命令对象实现为“重”模式。

命令模式可以与组合模式结合,即用组合模式将命令对象组合成复合命令对象。

结构

命令模式是一种比较灵活的模式,下面是经典命令模式的类图:

命令

模式的参与者:

1、命令接口,所有命令对象须实现此抽象——ICommand;

2、具体的命令,命令通常包含一个动作执行者——Command;

3、引发动作的对象,经常是UI组件或定时组件——Invoker;

4、动作的实际执行者——Receiver;

5、创建命令并设置接收者——Client;

上图中,除ICommand接口以外,每个参与者都有可能存在多个。

示例

系统中有很多批处理的任务,比如定期数据清理与备份,定期对某些数值进行一些计算等等,这些动作通常由一个对象(比如一个定时器)来调度,不同的动作也由不同的对象去执行。这些的动作的经常会发生增减,因此让调度对象包含所有执行者的引用,直接调用的执行者的方法,是不太现实的。同样由于希望动作的触发与动作的执行,希望构成异步,因此使用命令模式,将这些动作包装成命令对象,由这些命令对象包含具体的动作执行者的做法是可行的。

由于是一个说明性示例,所以下面的代码忽略了定时调度、多线程等具体实现。

1、动作执行者,下面提供两个。

计算器Calculater。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 计算器
 7:      /// </summary>
 8:      public class Calculater
 9:      {
10:          public void Calculate()
11:          {
12:              Console.WriteLine("计算操作");
13:          }
14:      }
15:  }
16:   

数据存储器DataStorage。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 数据存储
 7:      /// </summary>
 8:      public class DataStorage
 9:      {
10:          public void DataClear()
11:          {
12:              Console.WriteLine("清理冗余数据");
13:          }
14:   
15:          public void Backup()
16:          {
17:              Console.WriteLine("数据备份");
18:          }
19:      }
20:  }
21:   

2、命令接口ICommand。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 命令接口
 7:      /// </summary>
 8:      public interface ICommand
 9:      {
10:          void Execute();
11:      }
12:  }
13:   

3、具体的命令,示例中包含三个命令。

数据备份BackupCommand。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 数据备份命令
 7:      /// </summary>
 8:      public class BackupCommand : ICommand
 9:      {
10:          private DataStorage storage;
11:   
12:          public BackupCommand(DataStorage storage)
13:          {
14:              this.storage = storage;
15:          }
16:   
17:          public void Execute()
18:          {
19:              storage.Backup();
20:          }
21:      }
22:  }
23:   

数据清理DataClearCommand。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 数据清理命令
 7:      /// </summary>
 8:      public class DataClearCommand : ICommand
 9:      {
10:          private DataStorage storage;
11:   
12:          public DataClearCommand(DataStorage storage)
13:          {
14:              this.storage = storage;
15:          }
16:   
17:          public void Execute()
18:          {
19:              storage.DataClear();
20:          }
21:      }
22:  }
23:   

数据计算CalculateCommand。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      /// <summary>
 6:      /// 计算命令
 7:      /// </summary>
 8:      public class CalculateCommand : ICommand
 9:      {
10:          private Calculater calculater;
11:   
12:          public CalculateCommand(Calculater calculater)
13:          {
14:              this.calculater = calculater;
15:          }
16:   
17:          public void Execute()
18:          {
19:              calculater.Calculate();
20:          }
21:      }
22:  }
23:   

4、命令对象的管理与调用类Processor。

 1:  using System;
 2:  using System.Collections.Generic;
 3:   
 4:  namespace DesignPatterns.Command
 5:  {
 6:      /// <summary>
 7:      /// 执行者
 8:      /// </summary>
 9:      public class Processor
10:      {
11:          private List<ICommand> commands = new List<ICommand>();
12:   
13:          public void AddCommand(ICommand command)
14:          {
15:              this.commands.Add(command);
16:          }
17:   
18:          public void Do()
19:          {
20:              while (commands.Count > 0)
21:              {
22:                  commands[0].Execute();
23:                  commands.RemoveAt(0);
24:              }
25:          }
26:      }
27:  }
28:   

5、客户端代码,同时模拟了任务调度。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Command
 4:  {
 5:      class Program
 6:      {
 7:          static void Main(string[] args)
 8:          {
 9:              ICommand cmd1 = new DataClearCommand(new DataStorage());
10:              ICommand cmd2 = new CalculateCommand(new Calculater());
11:              ICommand cmd3 = new BackupCommand(new DataStorage());
12:   
13:              Processor processor = new Processor();
14:              processor.AddCommand(cmd1);
15:              processor.AddCommand(cmd2);
16:              processor.AddCommand(cmd3);
17:              processor.Do();
18:   
19:              Console.WriteLine("按任意键结束...");
20:              Console.ReadKey();
21:          }
22:      }
23:  }
24:   

6、运行,查看结果。

image

posted @ 2012-09-25 15:15  宽厚  阅读(1394)  评论(0编辑  收藏  举报