如何避免垂头丧气?(How to avoid downcast?)

2019-09-17 10:48发布

我有一个状态模式的实施每个国家处理它从事件队列中获取事件。 基State因此类有一个纯虚方法void handleEvent(const Event*) 活动继承基Event类,但每个事件都包含它的数据可以是不同类型(如int,串......或其他)的。 handleEvent需要确定接收到的事件的运行时类型,然后,以提取事件数据执行垂头丧气 。 动态创建活动,并存储在队列中(所以上溯造型发生在这里...)。

我知道,向下转换是一个不好的设计的征兆, 但有可能避免在这种情况下? 我想到的是访问者模式 ,其中基类国家将包含每个事件虚拟处理器但随后又垂头丧气需要发生在一段代码从队列中出队事件,并将其传递到当前状态。 (至少在这种情况下,大的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;
         ... 

      }
   }   
}

Answer 1:

典型的访问者模式执行没有垂头丧气,由于采用了双调度策略:

// 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);
}

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



Answer 2:

事件的想法是通过广义(和无关)接口传递详细对象。 垂头丧气是不可避免的,设计的一部分。 好还是坏,这是有争议的。

访问者模式只能从你隐藏铸造了。 它仍然在幕后,通过类型的虚方法地址解析执行。

因为你的Event已经有ID,这不是一个完全无关的类型,所以铸造是完全安全的。 在这里,你会亲自看的类型,在访问者模式你让编译器照顾这一点。

“无论上涨必须往下走”。



文章来源: How to avoid downcast?