8

各状態がイベントキューから取得したイベントを処理する状態パターンの実装があります。したがって、基本Stateクラスには純粋仮想メソッドがありvoid handleEvent(const Event*)ます。イベントは基本クラスを継承Eventしますが、各イベントには、異なるタイプ(int、stringなど)のデータが含まれています。イベントデータを抽出するにhandleEventは、受信したイベントのランタイムタイプを判別してから、ダウンキャストを実行する必要があります。イベントは動的に作成され、キューに保存されます(したがって、アップキャストはここで行われます...)。

ダウンキャストは悪い設計の兆候であることを私は知っていますが、この場合それを回避することは可能ですか?基本クラスのStateに各イベントの仮想ハンドラーが含まれるVisitorPatternを考えていますが、イベントをキューからデキューして現在の状態に渡すコードでダウンキャストを実行する必要があります。(少なくともこの場合、大きなswitch(eventID)ものは1か所だけになります...)。ビジターパターンは、ダウンキャストを回避するための最良の方法(ベストプラクティス)ですか?

これが擬似コードです(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);
}

ここで重要なのはv.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 に答える