一个Command模式的应用例子

我曾经写过一个小软件,故事是这样的:我们公司有一些设备在外面运行,有一天我接到一个任务,要求我编写一个算法对系统进行控制,改善系统的性能。根据大家的讨论很快制订了算法,我也很快编码完毕。但问题来了,我怎样进行测试,怎样验证这个算法呢?我可以在家里进行仿真,模拟系统的真实运行情况,但仿真终归仿真,我对算法是否真正有效心里没底;我更加不可能拿到外面测试,那可是商用系统,万一出了纰漏我可担当不起。

经过冥思苦想后(有一点夸张,呵呵),终于被我想出了一个办法。系统具有一样功能,可以由人机命令设置来进行数据采集,可以在运行期间产生运行记录存放在一个日志文件中,每一条运行记录对应着系统的一次执行状况,也对应着日志文件中的一行,具有固定的格式。在一个小时内,大约可以产生几千条这样的记录。

我要求系统维护人员在若干个典型时段进行采集,最后交到我手里就是几个日志文件,分别代表不同的时段系统运行的状况。假如我能够从这些日志文件中”还原“出系统的真实运行轨迹的话,那么我就可以有效的验证我们的算法。

我题目已经说了,这是一个运用Command模式的例子,因此我首先简单介绍一下Command模式。按照GoF的定义,Command模式的意图就是将一个请求封装为一个对象,从而你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,支持可撤销的操作。

Command模式的好处是可以将调用者和命令执行者解耦,有时候命令调用者并不知道命令要如何执行,例如你点击一个菜单项,但真正的执行是由应用程序决定。因此通过事先注册好的命令,利用多态技术,命令调用者即使不知道具体命令的类型也可以执行它(所有命令都提供一个Execute()的虚拟接口)。具体的Command对象是一个智能的对象,它知道相关的Context,所以它知道如何去执行具体的操作。

对我来说,我在这个项目中运用Command模式是想利用Command模式的另外一个优点,就是万一系统崩溃了,也可以通过已经备份日志来重新执行这些命令,从而重新”还原“整个系统。
现在我就有这些日志,我的目标就是通过它们来重新模拟整个系统的运行轨迹。

我的做法大致是这样的:
首先定义一个Command的抽象基类
struct Command {
    unsigned int timestamp;
    //other property
    virtual void execute () = 0;
    virtual ~Command() {}
};

然后定义3种不同类型的具体Command类
struct SuccessCommand : public Command {
    void execute ()
    {
        //do something...
      
    }
};

struct FailureCommand : public Command {
    void execute ()
    {
        //do something...
      
    }
};

struct TimerCommand : public Command {
    void execute ()
    {
        //do something...
       
    }
};

最后我定义一个std::vector< Command * >来存放这些Command的指针。
具体做法是从日志文件中读取满足特定条件的记录,比如说,某个小时内的所有记录。然后判断它们的状态是成功还是失败,来生成不同的命令。
std::vector< Command * >  comvec;
if (GetSysStatus(str) == SUCC)
{
    comvec.push_back(new SuccessCommand);
}
else
{
    comvec.push_back(new FailureCommand);
}

处理完毕后,因为我的算法是把1小时平均等分成若干个时间段,在时间段边界(例如每1分钟,可灵活设置)开始启动算法进行控制,所以我生成了若干个TimerCommand,添加到comvec中。
    //byPeriod为算法启动间隔,byHour为指定小时
    for (int i=byPeriod ; i<60; i+=byPeriod )
    {
        pCommand = new TimerCommand();
        pCommand->timestamp = byHour * 3600 * 1000 + i * 60 * 1000;
        //other processing
        comvec.push_back (pCommand);
    }
由于每个Command都有一个timestamp时间戳字段,因此我可以根据timestamp来进行排序,这个很简单:
sort (comvec.begin(), comvec.end(), LessTimeStamp);

LessTimeStamp是我自定义的一个比较函数,根据时间戳排序
bool LessTimeStamp (const Command *com1, const Command *com2)
{
    return com1->timestamp < com2->timestamp;
}

排完序后,一个符合我要求的Command序列就诞生了!现在我既得到了系统的真实运行轨迹,同时又把我的算法控制点插入其中,这样得到的仿真结果就是真实可信的。

vector< Command * >::iterator end = comvec.end();
for (vector< Command * >::iterator it = comvec.begin(); it != end; ++it)
{
    (*it)->execute();
}

Command模式是强大的,我的体会是要学习它的精髓,但不必太拘泥于它的实现形式,对其他模式也一样。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值