ACTIVE OBJECT 模式


ACTIVE OBJECT 模式

——《敏捷软件开发 原则、模式与实践(c#版)》第21章

    ACTIVE OBJECT 模式是建立在COMMAND模式的基础上。这是实现多线程控制的一项古老的技术。该模式有多种使用方式,为许多工业系统提供了一个简单的多任务核心。

    想法很简单。考虑代码清单1-1和代码清单1-2。ActiveObjectEngine 对象维护了一个 ICommand 对象的链表。用户可以向该引擎增加新的命令,或者调用 Run()。Run() 函数只是遍历链表,执行并去除每个命令。 

代码清单1-1

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Collections;

namespace  ActiveObject
{
    
public   class  ActiveObjectEngine
    {
        ArrayList itsCommands 
=   new  ArrayList();

        
public   void  AddCommand(ICommand aCommand)
        {
            itsCommands.Add(aCommand);
        }

        
public   void  Run()
        {
            
while  (itsCommands.Count  >   0 )
            {
                ICommand c 
=  (ICommand)itsCommands[ 0 ];
                itsCommands.RemoveAt(
0 );
                c.Execute();
            }
        }
    }
}

 

代码清单1-2 

     public   interface  ICommand
    {
        
void  Execute();
    }

 

    这似乎没有给人太深刻的印象。但是想象一下如果链表中的一个 ICommand 对象会克隆自己并把克隆对象放到链表的尾部,会发生什么呢?这个链表永远不会为空,Run() 函数永远不会返回。 

    考虑一下代码清单1-3中的测试用例。它创建了一个SleepCommand 对象。其中它向 SleepCommand 的构造函数中传了一个1,000ms的延迟。接着把 SleepCommand 对象放入到 ActiveObjectEngine 中。调用了 Run() 后,它将等待指定的毫秒数。

代码清单1-3

using  System;
using  System.Collections.Generic;
using  System.Text;
using  NUnit.Framework;

namespace  ActiveObject
{
    [TestFixture]
    
public   class  TestSleepCommand
    {
        
private   class  WakeUpCommand : ICommand
        {
            
public   bool  executed  =   false ;

            
public   void  Execute()
            {
                
this .executed  =   true ;
            }
        }

        [Test]
        
public   void  TestSleep()
        {
            WakeUpCommand wakeup 
=   new  WakeUpCommand();
            ActiveObjectEngine e 
=   new  ActiveObjectEngine();
            SleepCommand c 
=   new  SleepCommand( 1000 , e, wakeup);
            e.AddCommand(c);

            DateTime start 
= DateTime.Now;
            e.Run();
            DateTime stop 
=  DateTime.Now;
            
double  sleepTime  =  (stop  -  start).TotalMilliseconds;

            Assert.IsTrue(sleepTime 
>=   1000 ,
                
string .Format( " SleepTime {0} expected > 1000 " ,sleepTime.ToString()));
            Assert.IsTrue(sleepTime 
<=   1100 ,
                
string .Format( " SleepTime {0} expected < 1100 " , sleepTime.ToString()));
            Assert.IsTrue(wakeup.executed, 
" Command Executed " );
        }
    }
}

 

    我们来仔细看看这个测试用例。SleepCommand 的构造函数有 3 个参数。

        第一个:是延迟的毫秒数;

        第二个:是在其中运行该命令的ActiveObjectEngine 对象;

        第三个:是一个名为 wakeup 的另一个命令对象。

    测试的意图是 SleepCommand 会等待指定数目的毫秒,然后执行 wakeup 命令。

    代码清单1-4展示了 SleepCommand 的实现。在执行时,SleepCommand 检查自己以前是否已经执行过,如果没有,就记录下开始时间。如果没有过延迟时间,就把自己再加到 ActiveObjectEngine 中。如果过了延迟时间,就把 wakeup 命令对象加到 ActiveObjectEngine 中。 

代码清单1-4

using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  ActiveObject
{
    
public   class  SleepCommand : ICommand
    {
        
private  ICommand wakeupCommand  =   null ;
        
private  ActiveObjectEngine engine  =   null ;
        
private   long  SleepTime  =   0 ;
        
private  DateTime startTime;
        
private   bool  started  =   false ;

        
public  SleepCommand( long  milliSeconds, ActiveObjectEngine e,
            ICommand wakeupCommand)
        {
            
this .SleepTime  =  milliSeconds;
            
this .engine  =  e;
            
this .wakeupCommand  =  wakeupCommand;
        }

        
public   void  Execute()
        {
            DateTime currentTime 
=  DateTime.Now;
            
if  ( ! started)
            {
                started 
=   true ;
                
this .startTime  =  currentTime;
                
this .engine.AddCommand( this );
            }
            
else
            {
                TimeSpan elapsedTime 
=  currentTime  -  startTime;
                
if  (elapsedTime.TotalMilliseconds  <  SleepTime)
                {
                    
this .engine.AddCommand( this );
                }
                
else
                {
                    
this .engine.AddCommand( this .wakeupCommand);
                }
            }
        }
    }
}

 

     我们可以对该程序和等待一个事件的多线程程序做一个类比。当多线程程序中的一个线程等待一个事件时,它通常使用一些操作系统调用来阻塞自己知道事件发生。代码清单1-4中的程序并没有阻塞。相反,如果所等待的(elapsedTime.TotalMilliseconds < SleepTime)这个事件没有发生,线程只是把自己放回到 ActiveObjectEngine 中。

    采用该技术的变体去构建多线程系统已经是并且将会一直是一个常见的实践。这种类型的线程成为 run-to-completion  任务(RTC),因为每个 Command 实例在下一个 Command 实例可以运行之前就运行完成了。RTC 的名字意味着 Command 实例不会阻塞。

    Command 实例一经运行就一定得完成的特性赋予了RTC线程有趣的优点,那就是它们共享同一个运行栈。和传统的多线程系统的线程不通,不必为每个RTC线程定义或者分配各自的运行时栈。这在需要大量线程的内存受限系统中是一个强大的优势。

    继续我们的例子,代码清单1-5展示了一个简单的程序,其中使用了SleepCommand 并展示了它的多线程行为。该程序成为 DelayedType 。

代码清单1-5

using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  ActiveObject
{
    
public   class  DelayedTyper : ICommand
    {
        
private   long  itsDelay;
        
private   char  itsChar;
        
private   static   bool  stop  =   false ;
        
private   static  ActiveObjectEngine engin  =
            
new  ActiveObjectEngine();

        
private   class  StopCommand : ICommand
        {
            
public   void  Execute()
            {
                DelayedTyper.stop 
=   true ;
            }
        }

        
public   static   void  Main( string [] args)
        {
            engin.AddCommand(
new  DelayedTyper( 100 ' A ' ));
            engin.AddCommand(
new  DelayedTyper( 300 ' B ' ));
            engin.AddCommand(
new  DelayedTyper( 500 ' C ' ));
            engin.AddCommand(
new  DelayedTyper( 700 ' D ' ));

            ICommand stopCommand 
=   new  StopCommand();
            engin.AddCommand(
new  SleepCommand( 2000 , engin, stopCommand));
            engin.Run();

            Console.ReadLine();
        }

        
public  DelayedTyper( long  delay,  char  c)
        {
            
this .itsDelay  =  delay;
            
this .itsChar  =  c;
        }

        
public   void  Execute()
        {
            Console.Write(itsChar);
            
if  ( ! stop)
            {
                DelayAndRepeat();
            }
        }

        
private   void  DelayAndRepeat()
        {
            engin.AddCommand(
new  SleepCommand(itsDelay, engin,  this ));
        }
    }
}

 

    请注意 DelayedType 实现了 ICommand 接口。 它的 execute 方法只是打印出在构造时传入的字符,检查 stop 标志,并在该标志没有被设置时调用 DelayAndRepeat。DelayAndRepeat 方法使用构造时传入的延迟构造了一个 SleepCommand 对象。难后把构造后的 SleepCommand 对象插入到 ActiveObjectEngine 中。

    该ICommand 对象的行为很容易预测。实际上,它维持着一个循环,在循环中重复地打印一个指定的字符并等待一个指定的延迟。当 stop 标志被设置时,就退出循环。

    DelayedTyper 的 Main 函数创建了一个 SleepCommand 对象,该对象会在一段时间后设置 stop 标志。运行该程序会打印出一个简单的由 A、B、C、D 组成的字符串。运行结果:

    ABCDAABAACABADAABACAABADACABAAABACADB

 

    End。 

 

    示例代码ActiveObject.rar 

 

转载于:https://www.cnblogs.com/bruceleeliya/archive/2009/06/18/1505951.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值