Active Object模式通常和command命令模式一起使用。这是一个非常古老的模式。我们先来看一个例子。
// Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <list>
#include <memory>
class Command
{
public:
virtual void exec() = 0;
};
class Copy : public Command
{
public:
virtual void exec() override
{
printf("Do copy...\n");
}
};
class Move : public Command
{
public:
virtual void exec() override
{
printf("Do move...\n");
}
};
class ActiveObjectEngine
{
public:
void addCommand(std::unique_ptr<Command> cmd)
{
m_Commands.push_back(std::move(cmd));
}
void run()
{
while (!m_Commands.empty())
{
const std::unique_ptr<Command>& cmd = m_Commands.front();
cmd->exec();
m_Commands.pop_front();
}
}
private:
std::list<std::unique_ptr<Command>> m_Commands;
};
int _tmain(int argc, _TCHAR* argv[])
{
std::unique_ptr<Command> c1(new Copy());
std::unique_ptr<Command> c2(new Move());
ActiveObjectEngine engine;
engine.addCommand(std::move(c1));
engine.addCommand(std::move(c2));
engine.run();
return 0;
}
上面的代码基本分两部分:
1. Command类
2. ActiveObjectEngine类
ActiveObjectEngine里面维护了一个列表,存放所有的Command,在run()函数里面,把每一个命令都运行一遍,然后删除。
那么这么做有什么作用呢?
比如,我们想是想一个SleepCommand总是延迟一定时间后执行。我们先尝试搞一个SleepCopy类:
class SleepCopy : public Command
{
public:
SleepCopy(ActiveObjectEngine& e, DWORD dwDelay) : m_Engine(e), m_dwStartTime(0), m_dwDelay(dwDelay)
{}
SleepCopy(const SleepCopy& cmd) : m_Engine(cmd.m_Engine), m_dwStartTime(cmd.m_dwStartTime), m_dwDelay(cmd.m_dwDelay)
{
}
virtual void exec() override
{
if (m_dwStartTime == 0)
{
m_dwStartTime = ::GetTickCount();
std::unique_ptr<Command> newCmd(new SleepCopy(*this));
m_Engine.addCommand(std::move(newCmd));
}
else
{
DWORD dwUsed = ::GetTickCount() - m_dwStartTime;
if (dwUsed >= m_dwDelay)
{
printf("Do sleep copy..., delayed: %d ms\n", dwUsed);
}
else
{
std::unique_ptr<Command> newCmd(new SleepCopy(*this));
m_Engine.addCommand(std::move(newCmd));
}
}
}
private:
DWORD m_dwStartTime;
DWORD m_dwDelay;
ActiveObjectEngine& m_Engine;
};
这个类比前面两个Command略显复杂。主要思想就是,保存一个起始时间,然后在exec函数里面,来检查一下,如果已经到了延时的时间,那么就触发动作,如果没有,那么就创建一个新的command放到ActiveObject对象里面。这样,ActiveObjectEngine的run函数又会跑一次。直到最终延时时间到,触发动作为止。
其实,我们仔细看一下上面的代码,ActiveObjectEngine就是循环依次调用Command。每次调用完毕,就把对应的command对象从列表里面删除。
如果某个对象需要多次被调用,那么可以在exec函数里面处理一下,比如上面的SleepCopy用到的技巧。反正关键就是,如果想到ActiveObjectEngine再调用一次,那么就往它的列表里面插入一个对象。
从ActiveObjectEngine的角度来看,就是每次调用完对象exec()后,就删除,然后对象的exec()可以自己根据情况处理,如果需要再调用一次,那么就再往ActiveObjectEngine里面增加一个对象(或者重新加入当前对象)。
整个过程都是单线程,从每个Command对象来看,其实是可以实现每次调用只处理一部分工作,然后再把对象加入Engine,等待下一次处理。这就好比是用单线程模拟了多线程环境。每次循环都处理掉一部分工作,直到所有任务完成,Engine也就完成目的了。
ActiveObject模式可以很好的时候命令的轮询工作,然后可以在单线程环境里面实现多线程(多任务)的功能。很古老又很好用的一种思想,很多地方可以使用它。值得好好研究一下。在某些场合有意想不到的效果。起码模拟出来的多线程是运行在单线程环境里面的,这可以减少很多因为多线程环境带来的问题,比如同步。。。
完整代码:
// Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <list>
#include <memory>
#include <windows.h>
class Command
{
public:
virtual void exec() = 0;
};
class ActiveObjectEngine
{
public:
void addCommand(std::unique_ptr<Command> cmd)
{
m_Commands.push_back(std::move(cmd));
}
void run()
{
while (!m_Commands.empty())
{
const std::unique_ptr<Command>& cmd = m_Commands.front();
cmd->exec();
m_Commands.pop_front();
}
}
private:
std::list<std::unique_ptr<Command>> m_Commands;
};
class Copy : public Command
{
public:
virtual void exec() override
{
printf("Do copy..., tick count: %d\n", ::GetTickCount());
}
};
class Move : public Command
{
public:
virtual void exec() override
{
printf("Do move..., tick count: %d\n", ::GetTickCount());
}
};
class SleepCopy : public Command
{
public:
SleepCopy(ActiveObjectEngine& e, DWORD dwDelay) : m_Engine(e), m_dwStartTime(0), m_dwDelay(dwDelay)
{}
SleepCopy(const SleepCopy& cmd) : m_Engine(cmd.m_Engine), m_dwStartTime(cmd.m_dwStartTime), m_dwDelay(cmd.m_dwDelay)
{
}
virtual void exec() override
{
if (m_dwStartTime == 0)
{
m_dwStartTime = ::GetTickCount();
std::unique_ptr<Command> newCmd(new SleepCopy(*this));
m_Engine.addCommand(std::move(newCmd));
}
else
{
DWORD dwUsed = ::GetTickCount() - m_dwStartTime;
if (dwUsed >= m_dwDelay)
{
printf("Do sleep copy..., delayed: %d ms\n", dwUsed);
}
else
{
std::unique_ptr<Command> newCmd(new SleepCopy(*this));
m_Engine.addCommand(std::move(newCmd));
}
}
}
private:
DWORD m_dwStartTime;
DWORD m_dwDelay;
ActiveObjectEngine& m_Engine;
};
int _tmain(int argc, _TCHAR* argv[])
{
ActiveObjectEngine engine;
std::unique_ptr<Command> c1(new Copy());
std::unique_ptr<Command> c2(new Move());
std::unique_ptr<Command> c3(new SleepCopy(engine, 5000));
engine.addCommand(std::move(c1));
engine.addCommand(std::move(c2));
engine.addCommand(std::move(c3));
engine.run();
return 0;
}