Dissecting Code

Wednesday, March 13, 2013

Visitor Pattern

Working on implementing a SceneGraph, I came across the problem of allowing for operations on each node, without polluting the Node class itself. For example, for debugging purposes, I might want to have each node print out its type. So, I would have a Node.getType function, which returns the type of the node.

But, I wouldn't want to have a Node.printType function that prints its type and also recursively traverses its children to print their types.

The visitor pattern does exactly this. Conceptually, it allows for a virtual function to be added to an entire class hierarchy, without having to put the function within the class hierarchy itself. Below is a example for a usage of the pattern. Note that double-dispatch won't be required in case the class hierarchy places a constraint on the visitors to treat the visited nodes transparently, which is perhaps too much of a restriction. 

#include <iostream>
#include <list>
#include <string>

using namespace std;

class Asteroid;
class MyAsteroid;

class IAsteroidVisitor
{
public:
    virtual void visit(Asteroid&) = 0;
    virtual void visit(MyAsteroid&) = 0;
};

class Asteroid
{
public:
    virtual string getType()
    {
        return "Asteroid";
    }

    virtual void accept(IAsteroidVisitor& visitor)
    {
        visitor.visit(*this);
    }
    
};

class MyAsteroid : public Asteroid
{
public:
    string getType() override
    {
        return "My Asteroid";
    }

    string doFunkyStuff()
    {
        return "I do something the normal asteroid can't!";
    }

    void accept(IAsteroidVisitor& visitor) override
    {
        visitor.visit(*this);
    }
};

class AsteroidList
{
private:
    list<Asteroid*> m_pAsteroidList;
public:
    AsteroidList()
    {
        m_pAsteroidList.push_back(new Asteroid());
        m_pAsteroidList.push_back(new MyAsteroid());
    }

    void accept(IAsteroidVisitor& visitor)
    {
        for (list<Asteroid*>::const_iterator i = m_pAsteroidList.cbegin(); i != m_pAsteroidList.cend(); i++)
        {
            (*i)->accept(visitor);
        }
    }
};

class AsteroidPrintVisitor : public IAsteroidVisitor
{
public:
    void visit(Asteroid& a) override
    {
        cout<<a.getType()<<endl;
    }

    void visit(MyAsteroid &ma) override
    {
        cout<<ma.getType()<<endl;
        cout<<ma.doFunkyStuff()<<endl;
    }
};

int main(int argc, char *argv[])
{
    AsteroidList al;
    AsteroidPrintVisitor p;
    al.accept(p);
    return 0;
}


Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]



<< Home