// file: visitor.cc // author: keller // purpose: illustrating a Visitor Pattern // // description: We illustrate abstract and concrete Container classes // that accept abstract and concrete Visitors, including // "higher-order" Visitors. // // note: All definitions are in-line in this file for conciseness. #include #include // // "global" parameters (singleton pattern) // class Parameters { public: static int containerSize; // size of the container static int testSize; // number of items in container static double scaleFactor; // scale factor (used by a Visitor) static int skipInterval; // skip Interval (used by a Visitor) }; int Parameters::containerSize = 100; int Parameters::testSize = 19; double Parameters::scaleFactor = 5; int Parameters::skipInterval = 4; // // abstract Visitor interface // template class Visitor { public: // Visit an item. // If value returned by visit is false, then container will ask visitor to // go on and visit next item automatically. virtual bool visit(Item& item) { } }; // // abstract Container interface // Note: This Container is restricted to accomodate one Visitor at a time. // template class Container // interface { public: // Add an Item to the container. virtual bool add(Item item) = 0; // Accept a Visitor in the Container. virtual void acceptVisitor(Visitor* visitor) = 0; // Step the Visitor to the next Item. virtual bool stepVisitor() = 0; // Visit all of the Items in the container. virtual void visitAll() = 0; // Show the contents of the container. virtual void show(ostream &out) = 0; }; // // fixed-capacity container of Item // template class FixedContainer : public Container { private: Item *items; // the Items int number; // the current number of items int capacity; // the maximum number of items Visitor *visitor; // pointer to a visitor int numberVisited; // number of items visited public: // Construct a FixedContainer with a given capacity. FixedContainer(int capacity) { items = new Item[capacity]; this->capacity = capacity; number = 0; } // Add a new Item to the container. bool add(Item item) { if( number >= capacity ) return false; items[number++] = item; return true; } // Accept a Visitor to the container. void acceptVisitor(Visitor* visitor) { this->visitor = visitor; numberVisited = 0; } // Step the visitor until it returns true. // Return true if there are more unvisited items. bool stepVisitor() { while( numberVisited < number && !visitor->visit(items[numberVisited++]) ) { // keep visiting until a visit turns up true or all visited } return numberVisited < number; } // Visit all the items in one call. void visitAll() { while( stepVisitor() ) { } } // Show all items in the container. void show(ostream &out) { for( int i = 0; i < number; i++) out << items[i] << " "; out << endl; } }; // This Visitor squares each item in place. class SquaringVisitor : public Visitor { public: bool visit(double& item) { item = item*item; return true; // stop after each step } }; // This Visitor scales each item by the scale factor given in the constructor. class ScalingVisitor : public Visitor { private: double scaleFactor; public: ScalingVisitor(double scaleFactor) { this->scaleFactor = scaleFactor; } bool visit(double& item) { item *= scaleFactor; return true; // stop after each step } }; // This Visitor negates each item in place. class NegatingVisitor : public Visitor { public: bool visit(double& item) { item = -item; return true; } }; // This "higher-order" Visitor applies another Visitor that is passed in the // constructor to every Nth item, starting with the first, where N is also // passed in the constructor. class EveryNthVisitor : public Visitor { private: int count; int N; Visitor* visitor; public: EveryNthVisitor(int N, Visitor* visitor) { this->N = N; count = 0; this->visitor = visitor; } bool visit(double& item) { if( count == 0 ) { visitor->visit(item); } count = (count+1)%N; } }; // // test program // The output commands comment on what is being done. // int main(int argc, char** argv) { FixedContainer container(Parameters::containerSize); /*---------------------------------------------------------------------------*/ cout << "Populate the container with the integers 0 ... testSize." << endl << endl; for( int i = 0; i < Parameters::testSize; i++ ) { container.add((double)i); } container.show(cout); /*---------------------------------------------------------------------------*/ cout << endl << "Have the container accept a SquaringVisitor. Step it through " << endl << "each element in the container and show the container at each " << endl << "step." << endl << endl; container.acceptVisitor(new SquaringVisitor()); while( container.stepVisitor() ) { container.show(cout); } container.show(cout); /*---------------------------------------------------------------------------*/ cout << endl << "Have the container accept a ScalingVisitor. Have it visit " << endl << "all elements in the container, scaling each by a factor " << endl << "and show the container at the end." << endl << endl; container.acceptVisitor(new ScalingVisitor(Parameters::scaleFactor)); container.visitAll(); container.show(cout); /*---------------------------------------------------------------------------*/ cout << endl << "Have the container accept a Visitor that visits " << endl << "another Visitor, a NegatingVisitor, every so many elements, " << endl << "showing the container at end." << endl << endl; container.acceptVisitor(new EveryNthVisitor(Parameters::skipInterval, new NegatingVisitor())); while( container.stepVisitor() ) { // step the visitor through its paces } container.show(cout); } /* output: Populate the container with the integers 0 ... testSize. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Have the container accept a SquaringVisitor. Step it through each element in the container and show the container at each step. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 6 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 7 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 8 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 9 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 10 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 11 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 12 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 13 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 14 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 15 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 16 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 17 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 18 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 Have the container accept a ScalingVisitor. Have it visit all elements in the container, scaling each by a factor and show the container at the end. 0 5 20 45 80 125 180 245 320 405 500 605 720 845 980 1125 1280 1445 1620 Have the container accept a Visitor that visits another Visitor, a NegatingVisitor, every so many elements, showing the container at end. -0 5 20 45 -80 125 180 245 -320 405 500 605 -720 845 980 1125 -1280 1445 1620 */