I have to process individual objects of type InternalMessage, convert each one into some number of ExternalMessage objects (there is a one-to-many relationship between InternalMessages and ExternalMessages), and put these ExternalMessage objects on a queue to be processed. I want to have two modes of inserting onto this queue – batched, where all of the ExternalMessage are inserted onto the queue atomically, and normal insertion, where the ExternalMessage objects are inserted onto the queue as they are created. Note that there are multiple threads producing messages for this queue.
My thought on solving this is having an Inserter object, which handles the insertion:
class Inserter {
public:
void ProcessMessage(const ExternalMessage& externalMessage) = 0;
};
class SingleInserter {
public:
SingleInserter(Queue* q) : m_q(q) { }
void ProcessMessage(const ExternalMessage& externalMessage) {
m_q->Insert(q);
}
private:
Queue* m_q;
};
class BatchInserter {
public:
BatchInserter(Queue* q) : m_q(q) { }
void ProcessMessage(const ExternalMessage& externalMessage) {
m_cache.Insert(externalMessage);
}
~BatchInserter() {
m_q->BatchInsert(m_cache);
}
private:
Queue* m_q;
List<ExternalMessages> m_cache;
};
And then passing the Inserter down the call stack as messages are created.
BatchInserter ins;
or
SingleInserter ins;
and then
HandleMessage(someInternalMessage, ins);
and HandleMessage will do:
void HandleMessage(const InternalMessage& msg, Inserter& ins) {
//... do logic, eventually create a externalMsg (note - this could be further down
//the call stack, in a loop, etc. Multiple external messages could be created each
//call to HandleMessage)
ins.ProcessMessage(externalMsg);
}
Is this design ideal? It’s nice that it allows for different insertion types, but is there some way to do this without having to pass an object down the call stack (say, via template specialization)? Secondly, is there an STL pattern that I should be conforming to in implementing this?
It looks ok to me. Only change I could think of is making
HandleMessagea template. So you will havetemplate <class T> HandleMessage(const InternalMessage& msg, T& inserter). With this you will not be forcing the user of this class to derive from the abstract base classInserterand will avoid the little overhead caused by the dynamic dispatch due to virtual functions.