8

我有一个状态模式的实现,其中每个状态都处理它从事件队列中获取的事件。因此基State类有一个纯虚方法void handleEvent(const Event*)。事件继承基Event类,但每个事件都包含其可以是不同类型的数据(例如,int、string...或其他)。handleEvent必须确定接收到的事件的运行时类型,然后执行向下转换以提取事件数据。事件是动态创建并存储在队列中的(因此在这里进行向上转换......)

我知道向下转换是糟糕设计的标志,但在这种情况下是否可以避免它?我正在考虑访问者模式,其中基类 State 将包含每个事件的虚拟处理程序,但是再次向下转换将需要发生在从队列中取出事件并将其传递到当前状态的代码段中。(至少在这种情况下,大switch(eventID)只会在一个地方......)。访客模式是避免向下转换的最佳方式(最佳实践)吗?

这是伪代码(我boost::shared_ptr在这个例子中传递,但无论如何都会发生向下转换):

enum EventID
{
   EVENT_1,
   EVENT_2,
   ...
};

class Event
{
   EventID id;
public:
   Event(EventID id):id(id){}
   EventID id() const {return id;}
   virtual ~Event() = 0;
};

class Event1 : public Event
{
   int n;
public:
   Event1(int n):Event(EVENT_1), n(n){}
   int getN() const {return n;}
};

class Event2 : public Event
{
   std::string s;
public:
   Event2(std::string s):Event(EVENT_2), s(s){}
   std::string getS() const {return s;}
};

typedef boost::shared_ptr<Event> EventPtr;

class State
{
   ...
public:
   ...
   virtual ~State() = 0;
   virtual void handleEvent(const EventPtr& pEvent) = 0;
};

class StateA : public State
{
   ...
public:
   void handleEvent(const EventPtr& pEvent)
   {
      switch(pEvent->id())
      {
         case EVENT_1:        
            int n = boost::static_pointer_cast<Event1>(pEvent)->getN();
            ...
            break;
         case EVENT_2:
            std::string s = boost::static_pointer_cast<Event2>(pEvent)->getS();
            ...
            break;
         ... 

      }
   }   
}
4

2 回答 2

9

由于双重调度策略,典型的访问者模式不会表现出沮丧:

// Visitor.hpp
class EventBar;
class EventFoo;

class Visitor {
public:
    virtual void handle(EventBar const&) = 0;
    virtual void handle(EventFoo const&) = 0;
};

// Event.hpp
class Visitor;

class Event {
public:
    virtual void accept(Visitor&) const = 0;
};

和实现:

// EventBar.hpp
#include <Event.hpp>

class EventBar: public Event {
public:
    virtual void accept(Visitor& v);
};

// EventBar.cpp
#include <EventBar.hpp>
#include <Visitor.hpp>

void EventBar::accept(Visitor& v) {
    v.handle(*this);
}

这里的重点是在isv.handle(*this)的静态类型中*thisEventBar const&它选择了正确的virtual void handle(EventBar const&) = 0重载Visitor

于 2012-05-23T11:31:35.273 回答
3

事件的想法是通过通用(和不可知的)接口传递详细的对象。沮丧是不可避免的,也是设计的一部分。好与坏,这是有争议的。

访客模式只会将铸件隐藏在您面前。它仍然在幕后执行,类型通过虚拟方法地址解析。

因为你Event已经有了 id,所以它并不完全与类型无关,所以强制转换是非常安全的。在这里,您正在亲自查看类型,在访问者模式中,您正在让编译器处理它。

“凡涨必跌”。

于 2012-05-23T11:45:44.393 回答