Friday, November 12, 2010

A manager for heterogeneous data (the solution)

OK, so here's the solution I ended up with (forgive the improper syntax, you should still be able to get the picture):

class ZDataManager
{
    public:
       void RegisterListener(IListener* listener)
       { m_aListeners.push_back(listener); }
       void UnregisterListener(IListener* listener) 
       {m_aListeners.remove(listener);}
       void ProcessEvent(const SEventArgs& args) {
          foreach (list in m_aListeners) 
              if (list->GetArgType() == args.GetArgType())
                   list->ProcessEvent(args);
       }
    private: 
       std::vector<IListener*> m_aListeners;
}


class IListener
{
   virtual EArgType GetArgType() const = 0;
   virtual ProcessEvent(const SEventArgs&) = 0;
}


struct SEventArgs
{
   SEventArgs(const EArgType& eType) {m_eType = eType;}
   EArgType GetArgType() const {return m_eType;}
 private:
   EArgType m_eType;
}


struct SEventArgsFruit: public SEventArgs
{
    SEventArgsFruit(ETexture eText, EColor eCol, ETaste eTaste): SEventArgs(FRUIT_ARGS)
    { m_eText = eText; m_eCol = eCol; m_eTaste = eTaste};
    ETexture m_eText;
    EColor m_eColor;
    ETaste m_eTaste;
}


class ZFruitListener: public IListener
{
    ZFruitListener() {GetDataManager()->RegisterListener(this);}
    ~ZFruitListener() {GetDataManager()->UnregisterListener(this);}
    virtual EArgType GetArgType() const {return FRUIT_ARGS;}
    virtual ProcessEvent(const SEventArgs& args) {
       SEventArgsFruit* fruitArgs = static_cast<SEventArgsFruit*>(&args);
       //process new fruit
    }
}


class ZVegetableListener : public IListener
{
     virtual EArgType GetArgType() const {return VEGGIE_ARGS;}
     virtual ProcessEvent(const SEventArgs& args) {
        SEventArgsVeggie* veggieArgs = static_cast<SEventArgsVeggie*>(&args);
        //process new vegetable
     }
}



I'm quite satisfied with this solution, it relies on a static cast, but overall is still elegant, and does the job. The key to the solution is the generic SEventArgs struct and all of the wrappers that inherit from it, allowing us to package different types of data with an arbitrary number of parameters into something that can be processed generically thru the manager. It might seem awkward at first to be passing in a wrapper to the manager each time, or to create that wrapper for each case. But remember, we may have started with a solution that had a separate list for each arg type, and separate registration functions. Now, only in the ProcessEvent function do we begin to treat the args differently. 
One assumption I make (true for my set of use cases) that multiple listeners could listen to the same type of data, and one listener cannot listen to 2 different types of data. We could also have a base class ZListenerBase (inheriting from IListener), which simplifies the registration (this registration / unregistration happens exactly in the same way in the constructor / destructor of the listener classes). 

No comments:

Post a Comment