What we do here is utilize a flyweight because of the following assumptions:
Assumption: Rooms have sides with cardinality of [4] Assumption: The side can be either a door or a wall Assumption: The room has at least one (1) door
Unless it is an extreme maze, there is a high probability that there are more walls in a room than doors. By using the Flyweight pattern we can reduce our instance requirements by ( rooms * (sides==walls) ) - 1, for example:
20 rooms with an average of 3 walls each saves 59 wall instances!
The memory savings are the walls * sizeof( wall ) in general and the cycle savings are the new and deletes needed to instantiate and destroy each wall.
Then there is the probable reduction in the locality of reference faults when actually doing something with a wall. But that is beyond the scope of this example.
Anyway, in this example we play with pre-building a room with three (3) walls each. In addition, we utilize the Room interface of setting a side to the room instance.
Refer to ex9 for some of the first order class implementations. You will notice that we reuse almost everything from ex9 except for the Wall (in theory), the MazeFactory for creating the Wall, and the changes in the examp10.cpp source.
A more realistic implementation would have a MazeFactory that is configurable to register allocators so we don't need to expose the application to whether it is using a Flyweight or a true Class instance. Stay tuned, with Builder, Factory Method and a few other patterns we will achieve this and more.
/* CoreLinux++ Copyright (C) 2000 CoreLinux Consortium The CoreLinux++ Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The CoreLinux++ Library 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 GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <Common.hpp> #include <MazeFactory.hpp> #include <WallFactory.hpp> #include <Vector.hpp> #include <Map.hpp> using namespace corelinux; #include <iostream> #include <exception> CORELINUX_VECTOR( DoorPtr , DoorVector ); CORELINUX_MAP( RoomNumber, RoomPtr, less<RoomNumber> , RoomMap ); // // In module function prototypes // int main( void ); void doWork( MazeFactoryRef , WallFactoryRef ); // // Functions that work with Engine types // void handleAssertion( AssertionCref ); void handleException( ExceptionCref ); int main( void ) { // // Practice gracefull exception management // cout << endl; try { MazeFactory aFactory; WallFactory aWallFactory; doWork( aFactory, aWallFactory ); } catch( AssertionRef aAssert ) { handleAssertion(aAssert); } catch( ExceptionRef aException ) { handleException(aException); } catch( std::exception & e ) { cerr << e.what() << endl; } catch( ... ) { cerr << "Unknown exception." << endl; } return 0; } void displayMenu( void ) { cout << endl; cout << "\tCreate a Room 1" << endl; cout << "\tCreate a Wall 2" << endl; cout << "\tCreate a Room 3" << endl; cout << "\tQuit the example 4" << endl; cout << endl; } Int getCommand( void ) { displayMenu(); Int aOption; cout << "Enter the option number on the right to execute : "; cin >> aOption; return aOption; } void doWork( MazeFactoryRef aFactory, WallFactoryRef aWallFactory ) { bool keepWorking(true); DoorVector doors; RoomMap rooms; do { Int aCommand( getCommand() ); if( aCommand > 4 || aCommand < 0 ) { cerr << "You can't enter non-numeric options!" << endl; aCommand = 4; } else { ; // do nothing } switch( aCommand ) { // // Create a new room and insure that the same // doesn't already exist // case 1: { RoomNumber aNumber(0); cout << endl; cout << "Enter a RoomNumber for the new room : "; cin >> aNumber; if( rooms.find(aNumber) == rooms.end() ) { RoomPtr aRoom( aFactory.createRoom(aNumber) ); rooms[aNumber] = aRoom ; // // To bring this into reality, we will look // to the best case scenario and auto create // three (3) walls per room, all doors lead // to the east! // aRoom->setSide( NORTH, aWallFactory.createType() ); aRoom->setSide( SOUTH, aWallFactory.createType() ); aRoom->setSide( WEST, aWallFactory.createType() ); } else { cerr << "Room " << aNumber << " already exists!" << endl; } break; } // // Create a Wall. This is where we saved our job by // getting the maze to scale. // case 2: { IGNORE_RETURN aWallFactory.createType(); cout << endl; cout << "You now have " << aWallFactory.getAllocateCount() << " wall" << ( aWallFactory.getAllocateCount() > 1 ? "s." : "." ) << endl; cout << endl; break; } // // Create a door, we need two (2) valid rooms that the door // connects. But there is a bug here. Get involved, fix // the sample, become part of the open development process. // case 3: { RoomNumber aFirstNumber(0); RoomNumber aSecondNumber(0); cout << endl; cout << "Enter the first room the door connects : "; cin >> aFirstNumber; cout << endl; cout << "Enter the second room the door connects : "; cin >> aSecondNumber; if( rooms.find(aFirstNumber) == rooms.end() || rooms.find(aSecondNumber) == rooms.end() ) { cerr << "You need to enter valid room numbers." << endl; } else { doors.push_back ( aFactory.createDoor ( (*rooms.find(aFirstNumber)).second, (*rooms.find(aSecondNumber)).second ) ); cout << "You now have " << doors.size() << " door" << ( doors.size() > 1 ? "s." : "." ) << endl; } break; } // // Add a parent to an object // case 4: keepWorking=false; break; default: ; //do nothing break; } } while( keepWorking == true ); // // Now we can display info and clean up // cout << endl; cout << "Pre-cleanup Factory Statistics" << endl; cout << "==============================" << endl; cout << "Total Creates : " << aFactory.getTotalAllocates() << endl; cout << "Total Destroys : " << aFactory.getTotalDeallocates() << endl; cout << "Room Creates : " << rooms.size() << endl; cout << "Door Creates : " << doors.size() << endl; cout << "Wall Creates : " << aWallFactory.getAllocateCount() << endl; cout << "\t For a general memory saving of : " << sizeof( Wall ) * aWallFactory.getAllocateCount() << " bytes!" << endl; cout << endl; // // Clean out doors // DoorVectorIterator dItr( doors.begin() ); while( dItr != doors.end() ) { aFactory.destroyDoor( (*dItr ) ); ++dItr; } doors.clear(); // // Clean out walls, but wait!!! We don't have to // // // Clean out rooms // RoomMapIterator rItr( rooms.begin() ); while( rItr != rooms.end() ) { aFactory.destroyRoom( (*rItr).second ); ++rItr; } rooms.clear(); // // Final statistics // cout << endl; cout << "Post-cleanup Factory Statistics" << endl; cout << "===============================" << endl; cout << "Total Creates : " << aFactory.getTotalAllocates() << endl; cout << "Total Destroys : " << aFactory.getTotalDeallocates() << endl; cout << endl; } // // Peform default (just show it) // void handleAssertion( AssertionCref aAssert ) { cerr << aAssert.getFile() << ":" << aAssert.getLine() << ":" << "Assertion: "; if( aAssert.getType() == Assertion::NEVERGETHERE ) { cerr << "NEVER_GET_HERE"; } else { if( aAssert.getType() == Assertion::REQUIRE ) { cerr << "REQUIRE"; } else if( aAssert.getType() == Assertion::ENSURE ) { cerr << "ENSURE"; } else if( aAssert.getType() == Assertion::CHECK ) { cerr << "CHECK"; } else { cerr << "ASSERT"; } cerr << "( " << aAssert.getWhy() << " )"; } cerr << endl; } void handleException( ExceptionCref aExcp ) { cerr << aExcp.getFile() << ":" << aExcp.getLine() << ":" << "Exception: " << aExcp.getWhy() << endl; } /* Common rcs information do not modify $Author: prudhomm $ $Revision: 1.3 $ $Date: 2000/08/31 22:51:01 $ $Locker: $ */