Fork me on GitHub

设计模式之美:Command(命令)

索引

别名

  • Action
  • Transaction

意图

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

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

结构

参与者

Command

  • 声明 Execute 操作的接口。

ConcreteCommand

  • 将一个接收者对象绑定于一个动作。
  • 调用接收者相应的操作,以实现 Execute。

Client

  • 创建一个具体 Command 对象并设定它的接收者。

Invoker

  • 要求 Command 执行请求。

Receiver

  • 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。

适用性

在以下情况下可以使用 Command 模式:

  • Command 模式是回调(callback)机制的一个面向对象的替代品。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。
  • 在不同的时刻指定、排列和执行请求。Command 对象可以有一个与初始请求无关的生存期。
  • 支持取消操作。需要定义 Unexecute 操作来取消 Execute 操作调用的效果。
  • 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。
  • 用构建在原语操作上的高层操作构造一个系统。例如构建事务(Transaction)系统。

效果

  • Command 模式将调用操作的对象与知道如何实现该操作的对象解耦。
  • Command 是 first-class 对象。它们可像其他的对象一样被操纵和扩展。
  • 可以将多个 Command 装配成一个复合 Command。
  • 增加新的 Command 很容易,因为无需改变已有的类

相关模式

  • Composite 模式可被用来实现 MacroCommand。
  • Memento 模式可用来保持某个状态,Command 用这一状态来取消它的效果。
  • 可以使用 Prototype 来拷贝 Command 对象。

实现

实现方式(一):直接注入 Receiver 对象,Command 决定调用哪个方法。

 1 namespace CommandPattern.Implementation1
 2 {
 3   public abstract class Command
 4   {
 5     public abstract void Execute();
 6   }
 7 
 8   public class ConcreteCommand : Command
 9   {
10     private Receiver _receiver;
11 
12     public ConcreteCommand(Receiver receiver)
13     {
14       _receiver = receiver;
15     }
16 
17     public override void Execute()
18     {
19       _receiver.Action();
20     }
21   }
22 
23   public class Receiver
24   {
25     public void Action()
26     {
27       // do something
28     }
29   }
30 
31   public class Invoker
32   {
33     private Command _cmd;
34 
35     public void StoreCommand(Command cmd)
36     {
37       _cmd = cmd;
38     }
39 
40     public void Invoke()
41     {
42       if (_cmd != null)
43       {
44         _cmd.Execute();
45       }
46     }
47   }
48 
49   public class Client
50   {
51     public void TestCase1()
52     {
53       Receiver receiver = new Receiver();
54       Command cmd = new ConcreteCommand(receiver);
55 
56       Invoker invoker = new Invoker();
57       invoker.StoreCommand(cmd);
58 
59       invoker.Invoke();
60     }
61   }
62 }

实现方式(二):注入 Receiver 的指定方法,Command 仅能调用该方法。

 1 namespace CommandPattern.Implementation2
 2 {
 3   public abstract class Command
 4   {
 5     public abstract void Execute();
 6   }
 7 
 8   public class ConcreteCommand : Command
 9   {
10     private Action _action;
11 
12     public ConcreteCommand(Action action)
13     {
14       _action = action;
15     }
16 
17     public override void Execute()
18     {
19       _action.Invoke();
20     }
21   }
22 
23   public class Receiver
24   {
25     public void Action()
26     {
27       // do something
28     }
29   }
30 
31   public class Invoker
32   {
33     private Command _cmd;
34 
35     public void StoreCommand(Command cmd)
36     {
37       _cmd = cmd;
38     }
39 
40     public void Invoke()
41     {
42       if (_cmd != null)
43       {
44         _cmd.Execute();
45       }
46     }
47   }
48 
49   public class Client
50   {
51     public void TestCase2()
52     {
53       Receiver receiver = new Receiver();
54       Command cmd = new ConcreteCommand(receiver.Action);
55 
56       Invoker invoker = new Invoker();
57       invoker.StoreCommand(cmd);
58 
59       invoker.Invoke();
60     }
61   }
62 }

实现方式(三):参数化 Command 构造。

 1 namespace CommandPattern.Implementation3
 2 {
 3   public abstract class Command
 4   {
 5     public abstract void Execute();
 6   }
 7 
 8   public class ConcreteCommand : Command
 9   {
10     private Action<string> _action;
11     private string _state;
12 
13     public ConcreteCommand(Action<string> action, string state)
14     {
15       _action = action;
16       _state = state;
17     }
18 
19     public override void Execute()
20     {
21       _action.Invoke(_state);
22     }
23   }
24 
25   public class Receiver
26   {
27     public void Action(string state)
28     {
29       // do something
30     }
31   }
32 
33   public class Invoker
34   {
35     private Command _cmd;
36 
37     public void StoreCommand(Command cmd)
38     {
39       _cmd = cmd;
40     }
41 
42     public void Invoke()
43     {
44       if (_cmd != null)
45       {
46         _cmd.Execute();
47       }
48     }
49   }
50 
51   public class Client
52   {
53     public void TestCase3()
54     {
55       Receiver receiver = new Receiver();
56       Command cmd = new ConcreteCommand(receiver.Action, "Hello World");
57 
58       Invoker invoker = new Invoker();
59       invoker.StoreCommand(cmd);
60 
61       invoker.Invoke();
62     }
63   }
64 }

实现方式(四):使用泛型减少 Command 子类。

 1 namespace CommandPattern.Implementation4
 2 {
 3   public abstract class Command
 4   {
 5     public abstract void Execute();
 6   }
 7 
 8   public class ConcreteCommand<T, S> : Command
 9   {
10     private Action<T, S> _action;
11     private T _state1;
12     private S _state2;
13 
14     public ConcreteCommand(Action<T, S> action, T state1, S state2)
15     {
16       _action = action;
17       _state1 = state1;
18       _state2 = state2;
19     }
20 
21     public override void Execute()
22     {
23       _action.Invoke(_state1, _state2);
24     }
25   }
26 
27   public class Receiver
28   {
29     public void Action(string state1, int state2)
30     {
31       // do something
32     }
33   }
34 
35   public class Invoker
36   {
37     private Command _cmd;
38 
39     public void StoreCommand(Command cmd)
40     {
41       _cmd = cmd;
42     }
43 
44     public void Invoke()
45     {
46       if (_cmd != null)
47       {
48         _cmd.Execute();
49       }
50     }
51   }
52 
53   public class Client
54   {
55     public void TestCase4()
56     {
57       Receiver receiver = new Receiver();
58       Command cmd = new ConcreteCommand<string, int>(
59         receiver.Action, "Hello World", 250);
60 
61       Invoker invoker = new Invoker();
62       invoker.StoreCommand(cmd);
63 
64       invoker.Invoke();
65     }
66   }
67 }

实现方式(五):使用弱引用代替对 Receiver 的强引用。

 1 namespace CommandPattern.Implementation5
 2 {
 3   public class WeakAction
 4   {
 5     public WeakAction(Action action)
 6     {
 7       Method = action.Method;
 8       Reference = new WeakReference(action.Target);
 9     }
10 
11     protected MethodInfo Method { get; private set; }
12     protected WeakReference Reference { get; private set; }
13 
14     public bool IsAlive
15     {
16       get { return Reference.IsAlive; }
17     }
18 
19     public object Target
20     {
21       get { return Reference.Target; }
22     }
23 
24     public void Invoke()
25     {
26       if (Method != null && IsAlive)
27       {
28         Method.Invoke(Target, null);
29       }
30     }
31   }
32 
33   public abstract class Command
34   {
35     public abstract void Execute();
36   }
37 
38   public class ConcreteCommand : Command
39   {
40     private WeakAction _action;
41 
42     public ConcreteCommand(Action action)
43     {
44       _action = new WeakAction(action);
45     }
46 
47     public override void Execute()
48     {
49       _action.Invoke();
50     }
51   }
52 
53   public class Receiver
54   {
55     public void Action()
56     {
57       // do something
58     }
59   }
60 
61   public class Invoker
62   {
63     private Command _cmd;
64 
65     public void StoreCommand(Command cmd)
66     {
67       _cmd = cmd;
68     }
69 
70     public void Invoke()
71     {
72       if (_cmd != null)
73       {
74         _cmd.Execute();
75       }
76     }
77   }
78 
79   public class Client
80   {
81     public void TestCase5()
82     {
83       Receiver receiver = new Receiver();
84       Command cmd = new ConcreteCommand(receiver.Action);
85 
86       Invoker invoker = new Invoker();
87       invoker.StoreCommand(cmd);
88 
89       invoker.Invoke();
90     }
91   }
92 }

实现方式(六):使 Command 支持 Undo 和 Redo。

如果 Command 提供方法逆转操作,例如 Undo 操作,就可以取消执行的效果。为达到这个目的,ConcreteCommand 类可能需要存储额外的状态信息。

这个状态包括:

接收者对象,它真正执行处理该请求的各操作。

接收者上执行操作的参数。

如果处理请求的操作会改变接收者对象中的某些值,那么这些值也必须先存储起来。接收者还必须提供一些操作,以使该命令可将接收者恢复到它先前的状态。

 1 namespace CommandPattern.Implementation6
 2 {
 3   public abstract class Command
 4   {
 5     public abstract void Execute();
 6     public abstract void Unexecute();
 7     public abstract void Reexecute();
 8   }
 9 
10   public class ConcreteCommand : Command
11   {
12     private Receiver _receiver;
13     private string _state;
14     private string _lastState;
15 
16     public ConcreteCommand(Receiver receiver, string state)
17     {
18       _receiver = receiver;
19       _state = state;
20     }
21 
22     public override void Execute()
23     {
24       _lastState = _receiver.Name;
25       _receiver.ChangeName(_state);
26     }
27 
28     public override void Unexecute()
29     {
30       _receiver.ChangeName(_lastState);
31       _lastState = string.Empty;
32     }
33 
34     public override void Reexecute()
35     {
36       Unexecute();
37       Execute();
38     }
39   }
40 
41   public class Receiver
42   {
43     public string Name { get; private set; }
44 
45     public void ChangeName(string name)
46     {
47       // do something
48       Name = name;
49     }
50   }
51 
52   public class Invoker
53   {
54     private Command _cmd;
55 
56     public void StoreCommand(Command cmd)
57     {
58       _cmd = cmd;
59     }
60 
61     public void Invoke()
62     {
63       if (_cmd != null)
64       {
65         _cmd.Execute();
66       }
67     }
68 
69     public void UndoInvoke()
70     {
71       if (_cmd != null)
72       {
73         _cmd.Unexecute();
74       }
75     }
76   }
77 
78   public class Client
79   {
80     public void TestCase6()
81     {
82       Receiver receiver = new Receiver();
83       Command cmd = new ConcreteCommand(receiver, "Hello World");
84 
85       Invoker invoker = new Invoker();
86       invoker.StoreCommand(cmd);
87 
88       invoker.Invoke();
89       invoker.UndoInvoke();
90     }
91   }
92 }

实现方式(七):使 MacroCommand 来管理 Command 序列。

MacroCommand 需要提供增加和删除子 Command 的操作。

  1 namespace CommandPattern.Implementation7
  2 {
  3   public abstract class Command
  4   {
  5     public abstract void Execute();
  6   }
  7 
  8   public class MacroCommand : Command
  9   {
 10     private List<Command> _cmdList = new List<Command>();
 11 
 12     public MacroCommand()
 13     {
 14     }
 15 
 16     public void Add(Command cmd)
 17     {
 18       _cmdList.Add(cmd);
 19     }
 20 
 21     public void Remove(Command cmd)
 22     {
 23       _cmdList.Remove(cmd);
 24     }
 25 
 26     public override void Execute()
 27     {
 28       foreach (var cmd in _cmdList)
 29       {
 30         cmd.Execute();
 31       }
 32     }
 33   }
 34 
 35   public class ConcreteCommand1 : Command
 36   {
 37     private Receiver _receiver;
 38 
 39     public ConcreteCommand1(Receiver receiver)
 40     {
 41       _receiver = receiver;
 42     }
 43 
 44     public override void Execute()
 45     {
 46       _receiver.Action1();
 47     }
 48   }
 49 
 50   public class ConcreteCommand2 : Command
 51   {
 52     private Receiver _receiver;
 53 
 54     public ConcreteCommand2(Receiver receiver)
 55     {
 56       _receiver = receiver;
 57     }
 58 
 59     public override void Execute()
 60     {
 61       _receiver.Action2();
 62     }
 63   }
 64 
 65   public class Receiver
 66   {
 67     public void Action1()
 68     {
 69       // do something
 70     }
 71 
 72     public void Action2()
 73     {
 74       // do something
 75     }
 76   }
 77 
 78   public class Invoker
 79   {
 80     private Command _cmd;
 81 
 82     public void StoreCommand(Command cmd)
 83     {
 84       _cmd = cmd;
 85     }
 86 
 87     public void Invoke()
 88     {
 89       if (_cmd != null)
 90       {
 91         _cmd.Execute();
 92       }
 93     }
 94   }
 95 
 96   public class Client
 97   {
 98     public void TestCase7()
 99     {
100       Receiver receiver = new Receiver();
101       Command cmd1 = new ConcreteCommand1(receiver);
102       Command cmd2 = new ConcreteCommand2(receiver);
103       MacroCommand macro = new MacroCommand();
104       macro.Add(cmd1);
105       macro.Add(cmd2);
106 
107       Invoker invoker = new Invoker();
108       invoker.StoreCommand(macro);
109 
110       invoker.Invoke();
111     }
112   }
113 }

设计模式之美》为 Dennis Gao 发布于博客园的系列文章,任何未经作者本人同意的人为或爬虫转载均为耍流氓。

posted @ 2014-03-27 21:26 匠心十年 阅读(...) 评论(...) 编辑 收藏