Jump to content

Single-serving visitor pattern

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by PierreCaboche (talk | contribs) at 02:31, 26 April 2004. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

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);


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;
}