《敏捷软件开发——原则、模式与实践》 
Robert C.Martin 著,邓辉译,孟岩审,清华大学出版社,2003年9月第1版
没有人天生就具有命令他人的权利。
——Denis Diderot(1713-1784,法国哲学家,百科全书编者)
在近几年记述过的所有设计模式中,我认为COMMAND模式是最简单、最优雅的模式之一。但是我们将会看到,这种简单性是带有欺骗性的。COMMAND模式的适用范围是非常宽广的。
如图13.1所示,COMMAND模式简单得几乎可笑。程序13.1中的代码并没有起到削弱这种印象的作用。该模式仅由一个具有惟一方法的接口组成,这似乎是荒谬的。
图13.1 COMMAND模式 
程序13.1 Command.java
从严格的面向对象意义上来讲,这种做法是被强烈反对的——因为它具有功能分解的味道。它把函数层面的任务提升到了类的层面。这简直是对面向对象的亵渎!然而,在这两个思维范式(paradigm)的碰撞处,有趣的事情发生了。
想法很简单。考虑程序13.2和程序13.3。ActiveObjectEngine对象维护了一个Command对象的链表。用户可以向该引擎(engine)增加新的命令,或者调用run()。run()函数只是遍历链表,执行并去除每个命令。
(原书中是java程序,现翻译为C#程序,需要C#2.0编译器。根据C#的惯例,接口名称以I开头,方法名称的第一个字母大写。)
程序13.2 ActiveObjectEngine.cs
 using System.Collections.Generic;
using System.Collections.Generic; 
 
 
 namespace Asd.Chap13
namespace Asd.Chap13 
 {
{ 
 public class ActiveObjectEngine
  public class ActiveObjectEngine 
 {
  { 
 Queue<ICommand> itsCommands = new Queue<ICommand>();
    Queue<ICommand> itsCommands = new Queue<ICommand>(); 
 
     
 public void AddCommand(ICommand c)
    public void AddCommand(ICommand c) 
 {
    { 
 itsCommands.Enqueue(c);
      itsCommands.Enqueue(c); 
 }
    } 
 
     
 public void Run()
    public void Run() 
 {
    { 
 while (itsCommands.Count > 0) itsCommands.Dequeue().Execute();
      while (itsCommands.Count > 0) itsCommands.Dequeue().Execute(); 
 }
    } 
 }
  } 
 }
} 
 程序13.3 Command.cs
 程序13.3 Command.cs 
 namespace Asd.Chap13
namespace Asd.Chap13 
 {
{ 
 public interface ICommand
  public interface ICommand 
 {
  { 
 void Execute();
    void Execute(); 
 }
  } 
 
 
 public static class Pub
  public static class Pub 
 {
  { 
 public static long TicksPerMillisecond = 10000;
    public static long TicksPerMillisecond = 10000; 
 }
  } 
 }
} 
 这似乎没有给人太深刻的印象。但是想象一下如果链表中一个Command对象会克隆自己并把克隆对象放到链表的尾部,会发生什么呢?这个链表永远不会为空,run()函数永远不会返回。
   这似乎没有给人太深刻的印象。但是想象一下如果链表中一个Command对象会克隆自己并把克隆对象放到链表的尾部,会发生什么呢?这个链表永远不会为空,run()函数永远不会返回。 
考虑一下程序13.4中的测试用例。它创建了一个SleepCommand对象。其中,它向SleepCommand的构造函数中传了一个1000ms的延迟。接着把SleepCommand对象放入到ActiveObjectEngine中。调用run()后,它等待指定数目的毫秒。
程序13.4 TestSleepCommand.cs
 using System;
using System; 
 using NUnit.Framework;
using NUnit.Framework; 
 using Asd.Chap13;
using Asd.Chap13; 
 
 
 namespace NUnit.Tests.Asd.Chap13
namespace NUnit.Tests.Asd.Chap13 
 {
{ 
 [TestFixture]
  [TestFixture] 
 public class SleepCommandTest
  public class SleepCommandTest 
 {
  { 
 class WakeupCommand : ICommand
    class WakeupCommand : ICommand 
 {
    { 
 public void Execute()
      public void Execute() 
 {
      { 
 commandExecuted = true;
        commandExecuted = true; 
 }
      } 
 }
    } 
 
     
 static bool commandExecuted;
    static bool commandExecuted; 
 
     
 [Test]
    [Test] 
 public void Sleep()
    public void Sleep() 
 {
    { 
 ICommand wakeup = new WakeupCommand();
      ICommand wakeup = new WakeupCommand(); 
 ActiveObjectEngine e = new ActiveObjectEngine();
      ActiveObjectEngine e = new ActiveObjectEngine(); 
 SleepCommand c = new SleepCommand(1000, e, wakeup);
      SleepCommand c = new SleepCommand(1000, e, wakeup); 
 e.AddCommand(c);
      e.AddCommand(c); 
 long start = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long start = DateTime.Now.Ticks / Pub.TicksPerMillisecond; 
 e.Run();
      e.Run(); 
 long stop = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long stop = DateTime.Now.Ticks / Pub.TicksPerMillisecond; 
 long sleepTime = stop - start;
      long sleepTime = stop - start; 
 Assert.IsTrue(sleepTime > 1000, "SleepTime " + sleepTime.ToString() + " expected > 1000");
      Assert.IsTrue(sleepTime > 1000, "SleepTime " + sleepTime.ToString() + " expected > 1000"); 
 Assert.IsTrue(sleepTime < 1100, "SleepTime " + sleepTime.ToString() + " expected < 1100");
      Assert.IsTrue(sleepTime < 1100, "SleepTime " + sleepTime.ToString() + " expected < 1100"); 
 Assert.IsTrue(commandExecuted, "Command Executed");
      Assert.IsTrue(commandExecuted, "Command Executed"); 
 }
    } 
 }
  } 
 }
} 
 我们来仔细看看这个测试用例。SleepCommand的构造函数有3个参数。第一个是延迟的毫秒数。第二个是在其中运行该命令的ActiveObjectEngine对象。最后一个是名为wakeup的另一个命令对象。测试的意图是SleepCommand会等待指定数目的毫秒,然后执行wakeup命令。
   我们来仔细看看这个测试用例。SleepCommand的构造函数有3个参数。第一个是延迟的毫秒数。第二个是在其中运行该命令的ActiveObjectEngine对象。最后一个是名为wakeup的另一个命令对象。测试的意图是SleepCommand会等待指定数目的毫秒,然后执行wakeup命令。 
程序13.5展示了SleepCommand的实现。在执行时,SleepCommand检查自己以前是否已经执行过,如果没有,就记录下开始时间。如果没有过延迟时间,就把自己再加到ActiveObjectEngine中。如果过了延迟时间,就把wakeup命令对象加到ActiveObjectEngine中。
程序13.5 SleepCommand.cs
 using System;
using System; 
 
 
 namespace Asd.Chap13
namespace Asd.Chap13 
 {
{ 
 public class SleepCommand : ICommand
  public class SleepCommand : ICommand 
 {
  { 
 ICommand wakeupCommand;
    ICommand wakeupCommand; 
 ActiveObjectEngine engine;
    ActiveObjectEngine engine; 
 long sleepTime;
    long sleepTime; 
 long startTime;
    long startTime; 
 bool started;
    bool started; 
 
     
 public SleepCommand(long milliseconds, ActiveObjectEngine e, ICommand wakeupCommand)
    public SleepCommand(long milliseconds, ActiveObjectEngine e, ICommand wakeupCommand) 
 {
    { 
 sleepTime = milliseconds;
      sleepTime = milliseconds; 
 engine = e;
      engine = e; 
 this.wakeupCommand = wakeupCommand;
      this.wakeupCommand = wakeupCommand; 
 }
    } 
 
     
 public void Execute()
    public void Execute() 
 {
    { 
 long currentTime = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long currentTime = DateTime.Now.Ticks / Pub.TicksPerMillisecond; 
 if (!started)
      if (!started) 
 {
      { 
 started = true;
        started = true; 
 startTime = currentTime;
        startTime = currentTime; 
 engine.AddCommand(this);
        engine.AddCommand(this); 
 }
      } 
 else if ((currentTime - startTime) < sleepTime)
      else if ((currentTime - startTime) < sleepTime) 
 {
      { 
 engine.AddCommand(this);
        engine.AddCommand(this); 
 }
      } 
 else
      else 
 {
      { 
 engine.AddCommand(wakeupCommand);
        engine.AddCommand(wakeupCommand); 
 }
      } 
 }
    } 
 }
  } 
 }
} 
 (请使用如下的命令编译以上程序:
   (请使用如下的命令编译以上程序: 
csc /t:library /out:SleepCommand.dll SleepCommand.cs ActiveObjectEngine.cs Command.cs
csc /t:library /out:TestSleepCommand.dll /r:c:\NUnit\bin\nunit.framework.dll /r:SleepCommand.dll TestSleepCommand.cs
其中 NUint 请到 http://www.nunit.org 下载。然后运行 nunit-gui.exe 的结果如下:
 
 
)
我们可以对该程序和等待一个事件的多线程程序做一个类比。当多线程程序中的一个线程等待一个事件时,它通常使用一些操作系统调用来阻塞自己直到事件发生。程序13.5中的程序并没有阻塞。相反,如果所等待的((currentTime-startTime)<sleepTime)这个事件没有发生,它只是把自己放回到ActiveObjectEngine中。
采用该技术的变体(variations)去构建多线程系统已经是并且将会一直是一个很常见的实践。这种类型的线程被称为run-to-completion任务(RTC),因为每个Command实例在下一个Command实例可以运行之前就运行完成了。RTC的名字意味着Command实例不会阻塞。
Command实例一经运行就一定得完成的特性赋予了RTC线程有趣的优点,那就是它们共享同一个运行时堆栈。和传统的多线程系统中的线程不同,不必为每个RTC线程定义或者分配各自的运行时堆栈。这在需要大量线程的内存受限系统中是一个强大的优势。
继续我们的例子,程序13.6展示一个简单的程序,其中使用了SleepCommand并展示了它的多线程行为。该程序被称为DelayedTyper。
程序13.6 DelayedTyper.cs
 using System;
using System; 
 
 
 namespace Asd.Chap13
namespace Asd.Chap13 
 {
{ 
 public class DelayedTyper : ICommand
  public class DelayedTyper : ICommand 
 {
  { 
 class StopCommand : ICommand
    class StopCommand : ICommand 
 {
    { 
 public void Execute()
      public void Execute() 
 {
      { 
 stop = true;
        stop = true; 
 }
      } 
 }
    } 
 
 
 long itsDelay;
    long itsDelay; 
 char itsChar;
    char itsChar; 
 static ActiveObjectEngine engine = new ActiveObjectEngine();
    static ActiveObjectEngine engine = new ActiveObjectEngine(); 
 static bool stop = false;
    static bool stop = false; 
 
 
 static void Main()
    static void Main() 
 {
    { 
 engine.AddCommand(new DelayedTyper(100, '1'));
      engine.AddCommand(new DelayedTyper(100, '1')); 
 engine.AddCommand(new DelayedTyper(300, '3'));
      engine.AddCommand(new DelayedTyper(300, '3')); 
 engine.AddCommand(new DelayedTyper(500, '5'));
      engine.AddCommand(new DelayedTyper(500, '5')); 
 engine.AddCommand(new DelayedTyper(700, '7'));
      engine.AddCommand(new DelayedTyper(700, '7')); 
 ICommand stopCommand = new StopCommand();
      ICommand stopCommand = new StopCommand(); 
 engine.AddCommand(new SleepCommand(20000, engine, stopCommand));
      engine.AddCommand(new SleepCommand(20000, engine, stopCommand)); 
 engine.Run();
      engine.Run(); 
 }
    } 
 
     
 public DelayedTyper(long delay, char c)
    public DelayedTyper(long delay, char c) 
 {
    { 
 itsDelay = delay;
      itsDelay = delay; 
 itsChar = c;
      itsChar = c; 
 }
    } 
 
     
 public void Execute()
    public void Execute() 
 {
    { 
 Console.Write(itsChar);
      Console.Write(itsChar); 
 if (!stop) DelayAndRepeat();
      if (!stop) DelayAndRepeat(); 
 }
    } 
 
     
 void DelayAndRepeat()
    void DelayAndRepeat() 
 {
    { 
 engine.AddCommand(new SleepCommand(itsDelay, engine, this));
      engine.AddCommand(new SleepCommand(itsDelay, engine, this)); 
 }
    } 
 }
  } 
 }
} 
 (编译:csc /t:exe /out:DelayedTyper.exe /r:SleepCommand.dll DelayedTyper.cs)
   (编译:csc /t:exe /out:DelayedTyper.exe /r:SleepCommand.dll DelayedTyper.cs) 
请注意DelayedTyper实现了Command接口。它的execute方法只是打印出在构造时传入的字符,检查stop标志,并在该标志没有被设置时调用delayAndRepeat。delayAndRepeat方法使用构造时传入的延迟构造了一个SleepCommand对象,再把构造后的SleepCommand对象插入ActiveObjectEngine中。
该Command对象的行为很容易预测。实际上,它维持着一个循环,在循环中重复地打印一个指定的字符并等待一个指定的延迟。当stop标志被设置时,就退出循环。
DelayedTyper的main函数创建了几个DelayedTyper的实例并把它们放入ActiveObjectEngine中,每个实例都有自己的字符和延迟。接着创建了一个SleepCommand对象,该对象会在一段时间后设置stop标志。运行该程序会打印出一个简单的由'1'、'3'、'5'以及'7'组成的字符串。再次运行该程序会打印出一个相似,但是有差别的字符串。这里是两次有代表性的运行结果(我用我机器上的运行结果代替了原书中的运行结果):
13571113115137113151113171513111311571...
13571113151317111315131171135111311571...
这些字符串之所以有差别是因为CPU时钟和实时时钟没有完美的同步。这种不可确定的行为是多线程系统的特点。
有人认为COMMAND模式不符合面向对象的思维范式(paradigm),因为它对函数的关注超过了类。这也许是真的,但是在实际的软件开发中,COMMAND模式是非常有用的。
2. Lavender, R.G., and D.C.Schmidt. Active Object: An Object Behavioral Pattern for Concurrent Programming, in "Pattern Languages of Program Design" (J.O.Coplien, J.Vlissides, and N.kerth, eds.). Reading, MA: Addision-Wesley, 1996.
Robert C.Martin 著,邓辉译,孟岩审,清华大学出版社,2003年9月第1版
第13章 COMMAND模式和ACTIVE OBJECT模式
没有人天生就具有命令他人的权利。
——Denis Diderot(1713-1784,法国哲学家,百科全书编者)
在近几年记述过的所有设计模式中,我认为COMMAND模式是最简单、最优雅的模式之一。但是我们将会看到,这种简单性是带有欺骗性的。COMMAND模式的适用范围是非常宽广的。
如图13.1所示,COMMAND模式简单得几乎可笑。程序13.1中的代码并没有起到削弱这种印象的作用。该模式仅由一个具有惟一方法的接口组成,这似乎是荒谬的。
| <<interface>> Command | 
| +do() | 
程序13.1 Command.java
public interface Command
{
  public void do();
}  但是,事实上,该模式横过了一条非常有趣的界线。而这个交界处正是所有有趣的复杂性之所在。大多数类都是一组方法和相应的一组变量的结合。COMMAND模式不是这样的。它只是封装了一个没有任何变量的函数。 从严格的面向对象意义上来讲,这种做法是被强烈反对的——因为它具有功能分解的味道。它把函数层面的任务提升到了类的层面。这简直是对面向对象的亵渎!然而,在这两个思维范式(paradigm)的碰撞处,有趣的事情发生了。
13.1 简单的COMMAND
(略,请参看原书)13.2 事务操作
(略,请参看原书)13.3 UNDO
(略,请参看原书)13.4 ACTIVE OBJECT模式
ACTIVE OBJECT模式是我最喜欢使用COMMAND模式的地方之一。这是实现多线程控制的一项古老技术。该模式有多种使用方式,为许多工业系统提供了一个简单的多任务核心。想法很简单。考虑程序13.2和程序13.3。ActiveObjectEngine对象维护了一个Command对象的链表。用户可以向该引擎(engine)增加新的命令,或者调用run()。run()函数只是遍历链表,执行并去除每个命令。
(原书中是java程序,现翻译为C#程序,需要C#2.0编译器。根据C#的惯例,接口名称以I开头,方法名称的第一个字母大写。)
程序13.2 ActiveObjectEngine.cs
 using System.Collections.Generic;
using System.Collections.Generic;  
  namespace Asd.Chap13
namespace Asd.Chap13  {
{  public class ActiveObjectEngine
  public class ActiveObjectEngine  {
  {  Queue<ICommand> itsCommands = new Queue<ICommand>();
    Queue<ICommand> itsCommands = new Queue<ICommand>();  
      public void AddCommand(ICommand c)
    public void AddCommand(ICommand c)  {
    {  itsCommands.Enqueue(c);
      itsCommands.Enqueue(c);  }
    }  
      public void Run()
    public void Run()  {
    {  while (itsCommands.Count > 0) itsCommands.Dequeue().Execute();
      while (itsCommands.Count > 0) itsCommands.Dequeue().Execute();  }
    }  }
  }  }
}  
  namespace Asd.Chap13
namespace Asd.Chap13  {
{  public interface ICommand
  public interface ICommand  {
  {  void Execute();
    void Execute();  }
  }  
  public static class Pub
  public static class Pub  {
  {  public static long TicksPerMillisecond = 10000;
    public static long TicksPerMillisecond = 10000;  }
  }  }
}  
 考虑一下程序13.4中的测试用例。它创建了一个SleepCommand对象。其中,它向SleepCommand的构造函数中传了一个1000ms的延迟。接着把SleepCommand对象放入到ActiveObjectEngine中。调用run()后,它等待指定数目的毫秒。
程序13.4 TestSleepCommand.cs
 using System;
using System;  using NUnit.Framework;
using NUnit.Framework;  using Asd.Chap13;
using Asd.Chap13;  
  namespace NUnit.Tests.Asd.Chap13
namespace NUnit.Tests.Asd.Chap13  {
{  [TestFixture]
  [TestFixture]  public class SleepCommandTest
  public class SleepCommandTest  {
  {  class WakeupCommand : ICommand
    class WakeupCommand : ICommand  {
    {  public void Execute()
      public void Execute()  {
      {  commandExecuted = true;
        commandExecuted = true;  }
      }  }
    }  
      static bool commandExecuted;
    static bool commandExecuted;  
      [Test]
    [Test]  public void Sleep()
    public void Sleep()  {
    {  ICommand wakeup = new WakeupCommand();
      ICommand wakeup = new WakeupCommand();  ActiveObjectEngine e = new ActiveObjectEngine();
      ActiveObjectEngine e = new ActiveObjectEngine();  SleepCommand c = new SleepCommand(1000, e, wakeup);
      SleepCommand c = new SleepCommand(1000, e, wakeup);  e.AddCommand(c);
      e.AddCommand(c);  long start = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long start = DateTime.Now.Ticks / Pub.TicksPerMillisecond;  e.Run();
      e.Run();  long stop = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long stop = DateTime.Now.Ticks / Pub.TicksPerMillisecond;  long sleepTime = stop - start;
      long sleepTime = stop - start;  Assert.IsTrue(sleepTime > 1000, "SleepTime " + sleepTime.ToString() + " expected > 1000");
      Assert.IsTrue(sleepTime > 1000, "SleepTime " + sleepTime.ToString() + " expected > 1000");  Assert.IsTrue(sleepTime < 1100, "SleepTime " + sleepTime.ToString() + " expected < 1100");
      Assert.IsTrue(sleepTime < 1100, "SleepTime " + sleepTime.ToString() + " expected < 1100");  Assert.IsTrue(commandExecuted, "Command Executed");
      Assert.IsTrue(commandExecuted, "Command Executed");  }
    }  }
  }  }
}  
 程序13.5展示了SleepCommand的实现。在执行时,SleepCommand检查自己以前是否已经执行过,如果没有,就记录下开始时间。如果没有过延迟时间,就把自己再加到ActiveObjectEngine中。如果过了延迟时间,就把wakeup命令对象加到ActiveObjectEngine中。
程序13.5 SleepCommand.cs
 using System;
using System;  
  namespace Asd.Chap13
namespace Asd.Chap13  {
{  public class SleepCommand : ICommand
  public class SleepCommand : ICommand  {
  {  ICommand wakeupCommand;
    ICommand wakeupCommand;  ActiveObjectEngine engine;
    ActiveObjectEngine engine;  long sleepTime;
    long sleepTime;  long startTime;
    long startTime;  bool started;
    bool started;  
      public SleepCommand(long milliseconds, ActiveObjectEngine e, ICommand wakeupCommand)
    public SleepCommand(long milliseconds, ActiveObjectEngine e, ICommand wakeupCommand)  {
    {  sleepTime = milliseconds;
      sleepTime = milliseconds;  engine = e;
      engine = e;  this.wakeupCommand = wakeupCommand;
      this.wakeupCommand = wakeupCommand;  }
    }  
      public void Execute()
    public void Execute()  {
    {  long currentTime = DateTime.Now.Ticks / Pub.TicksPerMillisecond;
      long currentTime = DateTime.Now.Ticks / Pub.TicksPerMillisecond;  if (!started)
      if (!started)  {
      {  started = true;
        started = true;  startTime = currentTime;
        startTime = currentTime;  engine.AddCommand(this);
        engine.AddCommand(this);  }
      }  else if ((currentTime - startTime) < sleepTime)
      else if ((currentTime - startTime) < sleepTime)  {
      {  engine.AddCommand(this);
        engine.AddCommand(this);  }
      }  else
      else  {
      {  engine.AddCommand(wakeupCommand);
        engine.AddCommand(wakeupCommand);  }
      }  }
    }  }
  }  }
}  
 csc /t:library /out:SleepCommand.dll SleepCommand.cs ActiveObjectEngine.cs Command.cs
csc /t:library /out:TestSleepCommand.dll /r:c:\NUnit\bin\nunit.framework.dll /r:SleepCommand.dll TestSleepCommand.cs
其中 NUint 请到 http://www.nunit.org 下载。然后运行 nunit-gui.exe 的结果如下:
)
我们可以对该程序和等待一个事件的多线程程序做一个类比。当多线程程序中的一个线程等待一个事件时,它通常使用一些操作系统调用来阻塞自己直到事件发生。程序13.5中的程序并没有阻塞。相反,如果所等待的((currentTime-startTime)<sleepTime)这个事件没有发生,它只是把自己放回到ActiveObjectEngine中。
采用该技术的变体(variations)去构建多线程系统已经是并且将会一直是一个很常见的实践。这种类型的线程被称为run-to-completion任务(RTC),因为每个Command实例在下一个Command实例可以运行之前就运行完成了。RTC的名字意味着Command实例不会阻塞。
Command实例一经运行就一定得完成的特性赋予了RTC线程有趣的优点,那就是它们共享同一个运行时堆栈。和传统的多线程系统中的线程不同,不必为每个RTC线程定义或者分配各自的运行时堆栈。这在需要大量线程的内存受限系统中是一个强大的优势。
继续我们的例子,程序13.6展示一个简单的程序,其中使用了SleepCommand并展示了它的多线程行为。该程序被称为DelayedTyper。
程序13.6 DelayedTyper.cs
 using System;
using System;  
  namespace Asd.Chap13
namespace Asd.Chap13  {
{  public class DelayedTyper : ICommand
  public class DelayedTyper : ICommand  {
  {  class StopCommand : ICommand
    class StopCommand : ICommand  {
    {  public void Execute()
      public void Execute()  {
      {  stop = true;
        stop = true;  }
      }  }
    }  
  long itsDelay;
    long itsDelay;  char itsChar;
    char itsChar;  static ActiveObjectEngine engine = new ActiveObjectEngine();
    static ActiveObjectEngine engine = new ActiveObjectEngine();  static bool stop = false;
    static bool stop = false;  
  static void Main()
    static void Main()  {
    {  engine.AddCommand(new DelayedTyper(100, '1'));
      engine.AddCommand(new DelayedTyper(100, '1'));  engine.AddCommand(new DelayedTyper(300, '3'));
      engine.AddCommand(new DelayedTyper(300, '3'));  engine.AddCommand(new DelayedTyper(500, '5'));
      engine.AddCommand(new DelayedTyper(500, '5'));  engine.AddCommand(new DelayedTyper(700, '7'));
      engine.AddCommand(new DelayedTyper(700, '7'));  ICommand stopCommand = new StopCommand();
      ICommand stopCommand = new StopCommand();  engine.AddCommand(new SleepCommand(20000, engine, stopCommand));
      engine.AddCommand(new SleepCommand(20000, engine, stopCommand));  engine.Run();
      engine.Run();  }
    }  
      public DelayedTyper(long delay, char c)
    public DelayedTyper(long delay, char c)  {
    {  itsDelay = delay;
      itsDelay = delay;  itsChar = c;
      itsChar = c;  }
    }  
      public void Execute()
    public void Execute()  {
    {  Console.Write(itsChar);
      Console.Write(itsChar);  if (!stop) DelayAndRepeat();
      if (!stop) DelayAndRepeat();  }
    }  
      void DelayAndRepeat()
    void DelayAndRepeat()  {
    {  engine.AddCommand(new SleepCommand(itsDelay, engine, this));
      engine.AddCommand(new SleepCommand(itsDelay, engine, this));  }
    }  }
  }  }
}  
 请注意DelayedTyper实现了Command接口。它的execute方法只是打印出在构造时传入的字符,检查stop标志,并在该标志没有被设置时调用delayAndRepeat。delayAndRepeat方法使用构造时传入的延迟构造了一个SleepCommand对象,再把构造后的SleepCommand对象插入ActiveObjectEngine中。
该Command对象的行为很容易预测。实际上,它维持着一个循环,在循环中重复地打印一个指定的字符并等待一个指定的延迟。当stop标志被设置时,就退出循环。
DelayedTyper的main函数创建了几个DelayedTyper的实例并把它们放入ActiveObjectEngine中,每个实例都有自己的字符和延迟。接着创建了一个SleepCommand对象,该对象会在一段时间后设置stop标志。运行该程序会打印出一个简单的由'1'、'3'、'5'以及'7'组成的字符串。再次运行该程序会打印出一个相似,但是有差别的字符串。这里是两次有代表性的运行结果(我用我机器上的运行结果代替了原书中的运行结果):
13571113115137113151113171513111311571...
13571113151317111315131171135111311571...
这些字符串之所以有差别是因为CPU时钟和实时时钟没有完美的同步。这种不可确定的行为是多线程系统的特点。
13.5 结论
COMMAND模式的简单性掩盖了它的多功能性。COMMAND模式可以应用于多种不同的美妙用途,范围涉及数据库事务操作、设备控制、多线程核心以及GUI的do/undo管理。有人认为COMMAND模式不符合面向对象的思维范式(paradigm),因为它对函数的关注超过了类。这也许是真的,但是在实际的软件开发中,COMMAND模式是非常有用的。
参考文献
1. Gamma, et al. Design Patterns. Reading, MA: Addision-Wesley, 1995.2. Lavender, R.G., and D.C.Schmidt. Active Object: An Object Behavioral Pattern for Concurrent Programming, in "Pattern Languages of Program Design" (J.O.Coplien, J.Vlissides, and N.kerth, eds.). Reading, MA: Addision-Wesley, 1996.
 
                    
                     
                    
                 
                    
                 

 
        

 
   
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号