Is this a pattern? Should it be?
- by Arkadiy
The following is more of a statement than a question - it describes something that may be a pattern. The question is: is this a known pattern? Or, if it's not, should it be?
I've had a situation where I had to iterate over two dissimilar multi-layer data structures and copy information from one to the other. Depending on particular use case, I had around eight different kinds of layers, combined in about eight different combinations:
A-B-C
B-C
A-C
D-E
A-D-E
and so on
After a few unsuccessful attempts to factor out the repetition of per-layer iteration code, I realized that the key difficulty in this refactoring was the fact that the bottom level needed access to data gathered at higher levels. To explicitly accommodate this requirement, I introduced IterationContext class with a number of get() and set() methods for accumulating the necessary information.
In the end, I had the following class structure:
class Iterator {
virtual void iterateOver(const Structure &dataStructure1, IterationContext &ctx) const = 0;
};
class RecursingIterator : public Iterator {
RecursingIterator(const Iterator &below);
};
class IterateOverA : public RecursingIterator {
virtual void iterateOver(const Structure &dataStructure1, IterationContext &ctx) const {
// Iterate over members in dataStructure1
// locate corresponding item in dataStructure2 (passed via context)
// and set it in the context
// invoke the sub-iterator
};
class IterateOverB : public RecursingIterator {
virtual void iterateOver(const Structure &dataStructure1, IterationContext &ctx) const {
// iterate over members dataStructure2 (form context)
// set dataStructure2's item in the context
// locate corresponding item in dataStructure2 (passed via context)
// invoke the sub-iterator
};
void main()
{
class FinalCopy : public Iterator {
virtual void iterateOver(const Structure &dataStructure1, IterationContext &ctx) const {
// copy data from structure 1 to structure 2 in the context,
// using some data from higher levels as needed
}
}
IterationContext ctx(dateStructure2);
IterateOverA(IterateOverB(FinalCopy())).iterate(dataStructure1, ctx);
}
It so happens that dataStructure1 is a uniform data structure, similar to XML DOM in that respect, while dataStructure2 is a legacy data structure made of various structs and arrays. This allows me to pass dataStructure1 outside of the context for convenience. In general, either side of the iteration or both sides may be passed via context, as convenient.
The key situation points are:
complicated code that needs to be invoked in "layers", with multiple combinations of layer types possible
at the bottom layer, the information from top layers needs to be visible.
The key implementation points are:
use of context class to access the data from all levels of iteration
complicated iteration code encapsulated in implementation of pure virtual function
two interfaces - one aware of underlying iterator, one not aware of it.
use of const & to simplify the usage syntax.