October 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Examples in Event Dispatching

  • December 30, 2005
  • By Radu Braniste
  • Send Email »
  • More Articles »

This is the last article [1] in a series describing possible implementations of a type safe event dispatching mechanism in the context of single-layered and multilayered dispatchers/receptors. This time, you'll learn how dispatchers can be stacked in layers and how events can be selectively sent to certain levels only or broadcasted. Two illustrative implementations are presented: a basic one introducing the main concepts, later refactored in a more refined implementation.

The Layered Approach Revisited

In the previous article, I introduced the concept of single-layered ED (Event Dispatcher) as opposed to multilayered ED:

"We will call an ED single-layered if between the Source of the Event and the final Receiver there is no Global Relay (in other words, if an event can be solved in the chains directly linked to the source). Otherwise, the ED is multilayered."

The following diagram exemplifies the multilayered model:

A multilayered system might have event sources possible at every level. Every layer hosts a relay (exceptionally more than one). The relay layout can be composed at runtime so that an event can circulate between relays in a certain flow. Every relay consumes events by further dispatching them to one or more Dispatchers (the Dispatcher/Receptor part was presented in the previous article). If the event is unknown, it is relayed to the next layer, if any, or the compiler detects the missing receptor.

Some scenarios:

  • Event A generated at Level 2->Relay2->Dispatcher2-> No Receptor found->relay to Level3->Relay3->Dispatcher3->No Receptor found->Compiling Error.
  • Event B generated at Level 2->Relay2->Dispatcher2-> Receptor found

The above architectural model serves well in cases when a high level of decoupling is needed. Relays are the single points of contact between modules, relieving modules from sending messages directly from one to another. On top of this, due to the type safe implementation, there is no price to pay for additional indirection/casting and the speed of execution is unaffected.

Notes:

  1. An undeclared event (Event F, for example) generates a compiling error.
  2. Relaying to the last Layer is equivalent to a broadcast (all the relays will be tested until a receptor is found).
  3. "No Receptor Found" triggering a compiler error can be easily changed into a catch-all strategy and possibly a policy can be used, the same way it was described in [1].
  4. A real broadcast can be implemented if more than one receptor is required for the same event in two different ways by using parallel workflows as described in [1].
  5. From the implementation point of view, there is almost no distinction among Relays (Global or Local), Dispatchers, and Receptors. They are different at the architectural level and different in the way they are aggregated.
  6. A Global Relay can be associated directly to a Local Relay and the code described in [1] can be plugged at the Local Relay level (for example, Relay Level 2 is a Global relay, Dispatcher 2 is actually a Local Relay, and Receptors B and C are the Dispatchers as described in [1]). This is useful in cases when a Local Relay "knows" more than one event; then, a single-layered ED offers all the required flexibility in how to relay the events horizontally. A simpler approach is to dispatch the events in a flat mode, as described in the accompanying code.

The Multilayered Approach: Implementation

Due to the similarity of Global and Local Relays, the implementation is almost the same as in [1], with two small cosmetic changes: This time Relay::relay is static no more and one Dispatcher/Relay (Basic) no longer inherits from Relay.

template <class D>
struct Relay
{
   Relay(const D& d) : d_(d)
   {}
   template<class E>
   void relay(const E& e)
   {
      d_.relay(e);
   }
private:
   D d_;
};


struct EvtA
{
   EvtA(char c) : e(c){}
   char e;
};

struct EvtB
{
   EvtB(char c) : e(c) {}
   char e;
};

struct EvtC
{
   EvtC(char c) : e(c) {}
   char e;
};

struct EvtD
{
   EvtD(char c) : e(c) {}
   char e;
};

struct BasicLayerDispatcher
{
   void relay(const EvtA& e)
   {
      std::cout << "BasicLayer: " << e.e <<std::endl;
   }
};

template <class R>
struct SecondLayerDispatcher : public Relay<R>
{
   SecondLayerDispatcher(const R& r) : Relay<R>(r){}

   using Relay<R>::relay;
   void relay(const EvtB& e)
   {
      std::cout << "2ndLayer: " << e.e <<std::endl;
   }
};

template <class R>
struct ThirdLayerDispatcher : public Relay<R>
{
   ThirdLayerDispatcher(const R& r) : Relay<R>(r){}

   using Relay<R>::relay;
   void relay((const EvtC& e)
   {
      std::cout << "3rdLayer: " << e.e <<std::endl;
   }
   void relay(const EvtD& e)
   {
      std::cout << "3rdLayer: " << e.e <<std::endl;
   }
};

The difference is in how Relays are handled:

namespace RelayGenerator1
{

   template <int I>
   struct RG
   {};

   template <>
   struct RG<0>
   {
      typedef BasicLayerDispatcher DISPATCHER;
      typedef Relay<DISPATCHER> RELAY;
      static RELAY getRelay()
      {
         return RELAY(DISPATCHER());
      }
   };

   template <>
   struct RG<1>
   {
      typedef SecondLayerDispatcher< RG<0>::RELAY > DISPATCHER;
      typedef Relay<DISPATCHER > RELAY;
      static RELAY getRelay()
      {
         return RELAY(DISPATCHER(RG<0>::getRelay()));
      }
   };

   template <>
   struct RG<2>
   {
      typedef ThirdLayerDispatcher< RG<1>::RELAY > DISPATCHER;
      typedef Relay<DISPATCHER > RELAY;
      static RELAY getRelay()
      {
         return RELAY(DISPATCHER(RG<1>::getRelay()));
      }
   };

   template <int L, class E>
   void relay(const E& e)
   {
      RG<L>::getRelay().relay(e);
   }
}
void action()
{
   EvtD e('w');

   RelayGenerator1::relay<2>(e);
}

Please note how the Relays are indexed and chained. A helper function (RelayGenerator1::relay) allows event dispatching to start from a user-specified level. For example, RelayGenerator1::relay<1> excludes both EvtC and EvtD.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel