Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Single-serving visitor pattern

From Wikipedia, the free encyclopedia

In computer programming, the single-serving visitor pattern is a design pattern. Its intent is to optimise the implementation of a visitor that is allocated, used only once, and then deleted (which is the case of most visitors).

Applicability

[edit]

The single-serving visitor pattern should be used when visitors do not need to remain in memory. This is often the case when visiting a hierarchy of objects (such as when the visitor pattern is used together with the composite pattern) to perform a single task on it, for example counting the number of cameras in a 3D scene.

The regular visitor pattern should be used 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 (for example, 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. In doing so, it is ensured 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 examples

[edit]

The single-serving visitor is called through the intermediate of static methods.

Without parameters, this is SingleServingVisitor::applyTo(e);, while with parameters, it is SingleServingVisitor::applyTo(e, a, b);.

If implemented as a singleton:

SingleServingVisitor::setA(a);
SingleServingVisitor::setB(b);
SingleServingVisitor::applyTo(e);

Consequences

[edit]

Pros

[edit]
  • No "zombie" objects. With a single-serving visitor, it is ensured that visitors are 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.

Cons

[edit]
  • Repeated allocation. At each call of the applyTo() method, a single-serving visitor is created then discarded, which is time-consuming. In contrast, the singleton only performs one allocation.

Implementation

[edit]

Basic implementation (without parameters)

[edit]
template <typename A, typename B>
class SingleServingVisitor {
protected:
    SingleServingVisitor() = default;
public:
    ~SingleServingVisitor() = default;

    static void applyTo(Element& e) {
        e.accept(SingleServingVisitor<A, B>());
    }

    virtual void visitA(A& a) = 0;
    virtual void visitB(B& b) = 0;
};

Passing parameters

[edit]

If the single-serving visitor has to be initialised, the parameters have to be passed through the static method:

static void applyTo(Element& elem, const A& a, const B& b)  {
    elem.accept(SingleServingVisitor<A, B>(a, b));
}

Implementation as a singleton

[edit]

This implementation ensures:

  • that there is at most one instance of the single-serving visitor
  • that the visitor can be accessed later
template <typename A, typename B>
class SingleServingVisitor {
protected:
    A a;
    B b;

    SingleServingVisitor() = default;

    // Note: instance() method need not to be public
    static SingleServingVisitor& instance() noexcept {
        static SingleServingVisitor ssv;
        return ssv;
    }
public:
    ~SingleServingVisitor() = default;

    static void applyTo(Element& e) noexcept {
        e.accept(instance());
    }

    // static methods to access parameters
    static void setA(const A& a) noexcept {
        this->a = a;
    }

    static void setB(const B& b) noexcept {
        this->b = b;
    }

    virtual void visitA(A& a) = 0;
    virtual void visitB(B& b) = 0;
};
[edit]