Visitor

From CDOT Wiki
Revision as of 22:02, 8 April 2007 by Snnaraid (talk | contribs) (Code Examples)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Visitor Pattern (Object Behavioral)

Represents an operation to be performed on the elements of an object Structure. Visitor lets you define a new operation withou changing the classes of the elements on which it operates.

Prupose

The purpose of the Visitor Pattern is to encapsulate an operation that you want to perform on the elements of a data structure. In this way, you can change the operation being performed on a structure without the need of changing the classes of the elements that you are operating on. Using a Visitor pattern allows you to decouple the classes for the data structure and the algorithms used upon them.

Each node in the data structure "accepts" a Visitor, which sends a message to the Visitor which includes the node's class. The visitor will then execute its algorithm for that element. This process is known as "Double Dispatching." The node makes a call to the Visitor, passing itself in, and the Visitor executes its algorithm on the node. In Double Dispatching, the call made depends upon the type of the Visitor and of the Host (data structure node), not just of one component.

UML Diagram

Visitor.jpg

The key is the Accept method in the ConcreteElement classes. The body of this method shows the double dispatching call, where the Visitor is passed in to the accept method, and that visitor is told to execute its visit method, and is handed the node by the node itself. This makes for very robust code, since all of the decision making as to what to execute where and when it taken care of by the dispatching.


Code Examples

Visitor pattern in C++:

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSG_NODEVISITOR
#define OSG_NODEVISITOR 1

#include <osg/Node>
#include <osg/Matrix>
#include <osg/FrameStamp>

namespace osg {

class Billboard;
class ClearNode;
class ClipNode;
class CoordinateSystemNode;
class Geode;
class Group;
class LightSource;
class LOD;
class MatrixTransform;
class OccluderNode;
class PagedLOD;
class PositionAttitudeTransform;
class Projection;
class ProxyNode;
class Sequence;
class Switch;
class TexGenNode;
class Transform;
class CameraNode;
class CameraView;

class OSG_EXPORT NodeVisitor : public virtual Referenced
{
    public:

        enum TraversalMode
        {
            TRAVERSE_NONE,
            TRAVERSE_PARENTS,
            TRAVERSE_ALL_CHILDREN,
            TRAVERSE_ACTIVE_CHILDREN
        };

        enum VisitorType
        {
            NODE_VISITOR = 0,
            UPDATE_VISITOR,
            EVENT_VISITOR,
            COLLECT_OCCLUDER_VISITOR,
            CULL_VISITOR
        };
       NodeVisitor(TraversalMode tm=TRAVERSE_NONE);

        NodeVisitor(VisitorType type,TraversalMode tm=TRAVERSE_NONE);

        virtual ~NodeVisitor();
        virtual void reset() {}
        inline void setVisitorType(VisitorType type) { _visitorType = type; }
        inline VisitorType getVisitorType() const { return _visitorType; }
        inline void setTraversalNumber(int fn) { _traversalNumber = fn; }
        inline int getTraversalNumber() const { return _traversalNumber; }
        inline void setFrameStamp(FrameStamp* fs) { _frameStamp = fs; }
        inline const FrameStamp* getFrameStamp() const { return _frameStamp.get(); }
        inline void setTraversalMask(Node::NodeMask mask) { _traversalMask = mask; }

        /** Get the TraversalMask.*/
        inline Node::NodeMask getTraversalMask() const { return _traversalMask; }
        inline void setNodeMaskOverride(Node::NodeMask mask) { _nodeMaskOverride = mask; }
       inline Node::NodeMask getNodeMaskOverride() const { return _nodeMaskOverride; }
       inline bool validNodeMask(const osg::Node& node) const
        {
            return (getTraversalMask() & (getNodeMaskOverride() | node.getNodeMask()))!=0;
        }

        /** Set the traversal mode for Node::traverse() to use when
            deciding which children of a node to traverse. If a
            NodeVisitor has been attached via setTraverseVisitor()
            and the new mode is not TRAVERSE_VISITOR then the attached
            visitor is detached. Default mode is TRAVERSE_NONE.*/
        inline void setTraversalMode(TraversalMode mode) { _traversalMode = mode; }

        /** Get the traversal mode.*/
        inline TraversalMode getTraversalMode() const { return _traversalMode; }

        inline void setUserData(Referenced* obj) { _userData = obj; }

        /** Get user data.*/
        inline Referenced* getUserData() { return _userData.get(); }

        /** Get const user data.*/
        inline const Referenced* getUserData() const { return _userData.get(); }


        inline void traverse(Node& node)
        {
            if (_traversalMode==TRAVERSE_PARENTS) node.ascend(*this);
            else if (_traversalMode!=TRAVERSE_NONE) node.traverse(*this);
        }

        /** Method called by osg::Node::accept() method before
          * a call to the NodeVisitor::apply(..).  The back of the list will,
          * therefore, be the current node being visited inside the apply(..),
          * and the rest of the list will be the parental sequence of nodes
          * from the top most node applied down the graph to the current node.
          * Note, the user does not typically call pushNodeOnPath() as it
          * will be called automatically by the Node::accept() method.*/
        inline void pushOntoNodePath(Node* node) { if (_traversalMode!=TRAVERSE_PARENTS) _nodePath.push_back(node); else _nodePath.insert(_nodePath.begin(),node); }

        inline void popFromNodePath()            { if (_traversalMode!=TRAVERSE_PARENTS) _nodePath.pop_back(); else _nodePath.erase(_nodePath.begin()); }

        /** Get the non const NodePath from the top most node applied down
          * to the current Node being visited.*/
        NodePath& getNodePath() { return _nodePath; }

        /** Get the const NodePath from the top most node applied down
          * to the current Node being visited.*/
        const NodePath& getNodePath() const { return _nodePath; }

        virtual osg::Vec3 getEyePoint() const { return Vec3(0.0f,0.0f,0.0f); }

        virtual float getDistanceToEyePoint(const Vec3& /*pos*/, bool /*useLODScale*/) const { return 0.0f; }

        virtual float getDistanceFromEyePoint(const Vec3& /*pos*/, bool /*useLODScale*/) const { return 0.0f; }

        virtual void apply(Node& node)                      { traverse(node);}

        virtual void apply(Geode& node)                     { apply((Node&)node); }
        virtual void apply(Billboard& node)                 { apply((Geode&)node); }

        virtual void apply(Group& node)                     { apply((Node&)node); }

        virtual void apply(ProxyNode& node)                 { apply((Group&)node); }

        virtual void apply(Projection& node)                { apply((Group&)node); }

        virtual void apply(CoordinateSystemNode& node)      { apply((Group&)node); }

        virtual void apply(ClipNode& node)                  { apply((Group&)node); }
        virtual void apply(TexGenNode& node)                { apply((Group&)node); }
        virtual void apply(LightSource& node)               { apply((Group&)node); }

        virtual void apply(Transform& node)                 { apply((Group&)node); }
        virtual void apply(CameraNode& node)                { apply((Transform&)node); }
        virtual void apply(CameraView& node)                { apply((Transform&)node); }
        virtual void apply(MatrixTransform& node)           { apply((Transform&)node); }
        virtual void apply(PositionAttitudeTransform& node) { apply((Transform&)node); }

        virtual void apply(Switch& node)                    { apply((Group&)node); }
        virtual void apply(Sequence& node)                  { apply((Group&)node); }
        virtual void apply(LOD& node)                       { apply((Group&)node); }
        virtual void apply(PagedLOD& node)                  { apply((LOD&)node); }
        virtual void apply(ClearNode& node)                 { apply((Group&)node); }
        virtual void apply(OccluderNode& node)              { apply((Group&)node); }


        /** Callback for managing database paging, such as generated by PagedLOD nodes.*/
        class DatabaseRequestHandler : public osg::Referenced
        {
        public:
            virtual void requestNodeFile(const std::string& fileName,osg::Group* group, float priority, const FrameStamp* framestamp) = 0;

        protected:
            virtual ~DatabaseRequestHandler() {}
        };

        /** Set the handler for database requests.*/
        void setDatabaseRequestHandler(DatabaseRequestHandler* handler) { _databaseRequestHandler = handler; }

        /** Get the handler for database requests.*/
        DatabaseRequestHandler* getDatabaseRequestHandler() { return _databaseRequestHandler.get(); }

        /** Get the const handler for database requests.*/
        const DatabaseRequestHandler* getDatabaseRequestHandler() const { return _databaseRequestHandler.get(); }



    protected:

        VisitorType                     _visitorType;
        int                             _traversalNumber;

        ref_ptr<FrameStamp>             _frameStamp;

        TraversalMode                   _traversalMode;
        Node::NodeMask                  _traversalMask;
        Node::NodeMask                  _nodeMaskOverride;

        NodePath                        _nodePath;

        ref_ptr<Referenced>             _userData;

        ref_ptr<DatabaseRequestHandler> _databaseRequestHandler;

};


/** Convenience functor for assisting visiting of arrays of osg::Node's.*/
struct NodeAcceptOp
{
    NodeVisitor& _nv;
    NodeAcceptOp(NodeVisitor& nv):_nv(nv) {}
    void operator () (Node* node) { node->accept(_nv); }
    void operator () (ref_ptr<Node> node) { node->accept(_nv); }
};

}

#endif



Visitor pattern in Java:


interface Visitor {
    void visit(Wheel wheel);
    void visit(Engine engine);
    void visit(Body body);
    void visit(Car car);
}

interface Visitable {
    public void accept(Visitor visitor);
}

class Wheel implements Visitable {
    private String name;
    Wheel(String name) {
        this.name = name;
    }
    String getName() {
        return this.name;
    }
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Engine implements Visitable{
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Body implements Visitable{
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Car implements Visitable {
    private Engine  engine = new Engine();
    private Body    body   = new Body();
    private Wheel[] wheels 
        = { new Wheel("front left"), new Wheel("front right"),
            new Wheel("back left") , new Wheel("back right")  };
    public Engine getEngine() {
        return this.engine;
    }
    public Body getBody() {
        return this.body;
    }
    public Wheel[] getWheels() {
        return this.wheels;
    }
    public void accept(Visitor visitor) {
        visitor.visit(this);
        engine.accept(visitor);
        body.accept(visitor);
        for(int i = 0; i < wheels.length; ++i) {
            wheels[i].accept(visitor);
        }
    }
}

class PrintVisitor implements Visitor {

    public void visit(Wheel wheel) {
        System.out.println("Visiting "+ wheel.getName()
                            + " wheel");
    }
    public void visit(Engine engine) {
        System.out.println("Visiting engine");
    }
    public void visit(Body body) {
        System.out.println("Visiting body");
    }
    public void visit(Car car) {
        System.out.println("Visiting car");
    }

}

public class VisitorDemo {
    static public void main(String[] args){
        Car car = new Car();
        Visitor visitor = new PrintVisitor();
        car.accept(visitor);
    }
}


Reference

http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/VisitorPattern.htm

http://en.wikipedia.org/wiki/Visitor_pattern