Single-serving visitor pattern
Intent
Optimise the implementation of a Visitor that is allocated, used only once, and then deleted (which is the case of most Visitors).
Applicability
Use the Single-Serving Visitor pattern when you would use a Visitor which does not need to remain in memory. This is often the case when visiting a hierarchy of objects (use of Visitor pattern together with Composite pattern) to perform a single task on it (eg: counting the number of cameras in a 3D-scene).
Use the regular Visitor pattern when the visitor must remain in memory. This occurs when the visitor is configured with a number of parameters that must be kept in memory for a later use of the visitor (eg: for storing the rendering options of a 3D-scene renderer).
However, if there should be only one instance of such a visitor in a whole program, it can be a good idea to implement it both as a Single-Serving Visitor and as a Singleton (see implementaion section). In doing so, we make sure that the Single-Serving Visitor can be called later with its parameters unchanged (in this particular case "Single-Serving Visitor" is an abuse of language since the visitor can be used several times).
Usage
The Single-Serving Visitor is called through the intermediate of static methods.
Exemples:
- without parameters
Element* elem; SingleServingVisitor::applyTo(elem);
- with parameters:
Element* elem; TYPE param1, param2;
SingleServingVisitor::applyTo(elem, param1, param2);
- implementation as a Singleton:
Element* elem; TYPE param1, param2;
SingleServingVisitor::setParam1(param1); SingleServingVisitor::setParam2(param2); SingleServingVisitor::applyTo(elem);
Consequences
+ No "zombie" objects. With a Single-Serving Visitor, we make sure that the visitor is allocated when needed and destroyed once useless.
+ A simpler interface than Visitor. The visitor is created, used and free by the sole call of the applyTo static method. It can't get any simpler than that!
- Repeated allocation. At each call of the applyTo method, a Single-Serving Visitor is created then discarded, which is time-consuming. Consider the implementation as a Singleton, which performs only one allocation.
Implementation (in C++)
Basic implementation (without parameters)
//// Declaration class Element; class ElementA; class ElementB; class SingleServingVisitor;
... // Same as Visitor pattern.
//// Definition
class SingleServingVisitor { protected: SingleServingVisitor(); public: ~SingleServingVisitor();
static void applyTo(Element*); virtual void visitElementA(ElementA*) = 0; virtual void visitElementB(ElementB*) = 0; }
//// Implementation
void SingleServingVisitor::applyTo(Element* elem){ SingleServingVisitor* ssv = new SingleServingVisitor(); elem->accept(ssv); delete ssv; }
Passing parameters
If the Single-Serving Visitor has to be initialised, the parameters have to be passed through the static method:
void SingleServingVisitor::applyTo(Element* elem, TYPE param1, TYPE param2, ...){ SingleServingVisitor* ssv = new SingleServingVisitor(param1, param2, ...); elem->accept(ssv); delete ssv; }
Implementation as a Singleton
This implementation ensures:
- that there is at most one instance of the Single-Serving Visitor
- that we can access the visitor later (to rese it and/or change parameters)
//// Definition
class SingleServingVisitor { protected: static SingleServingVisitor* _instance; TYPE _param1; TYPE _param2;
SingleServingVisitor();
static SingleServingVisitor* getInstance(); // Note: getInstance method does not need to be public
public: ~SingleServingVisitor();
static void applyTo(Element*);
// static methods to access parameters static void setParam1(TYPE); static void setParam2(TYPE);
virtual void visitElementA(ElementA*) = 0; virtual void visitElementB(ElementB*) = 0; }
//// Implementation
SingleServingVisitor* SingleServingVisitor::_instance = NULL;
SingleServingVisitor* SingleServingVisitor::getInstance() { if (_instance == NULL) _instance = new SingleServingVisitor(); return _instance; }
void SingleServingVisitor::applyTo(Element* elem){ elem->accept( getInstance() ); }
void SingleServingVisitor::setParam1(TYPE param1){ getInstance()->_param1 = param1; }
void SingleServingVisitor::setParam2(TYPE param2){ getInstance()->_param2 = param2; }
Related Patterns
- Visitor pattern, from which this pattern derives
- Composite pattern: Single-Serving Visitor is often applied to hierarchies of elements
- Singleton pattern