300 likes | 459 Vues
Object Oriented Analysis & Design. Class Design 2. Contents. Class Design. How you design your classes impacts The efficiency of your program The architecture of your program The understandability of your program The maintainability of your program The total cost of the game.
E N D
Object Oriented Analysis & Design Class Design 2
Class Design • How you design your classes impacts • The efficiency of your program • The architecture of your program • The understandability of your program • The maintainability of your program • The total cost of the game
The Well-Designed Class • A well-designed class can • Be used anywhere in the program • Be used in conjunction with STL containers • Provide all methods that will ever be needed to use the class in any situation • Be serialized if the data needs to be sent across a network or saved on disk • Provide other classes access to the data they need • Protect its data so other classes cannot corrupt it
The General Purpose Class • A class which can be used almost anywhere • Defines a parameterless constructor • Allows default instances to be created for making arrays, STL containers and serialization • Defines a copy constructor • Used when copying values to parameter lists and when returning classes from methods • An assignment operator • Allows one object to be copied to another object
The General Purpose Class class Weapon { private: std::string name; float killRadius; float damageFactor; public: Weapon(); // init all fields Weapon(const Weapon &other); // copy constructor Weapon operator=(Weapon &other); // assignment // other methods ... };
Classes for Use with STL • Classes used with STL must also define • Operator== • Operator!=
Classes for Use with STL class Weapon { private: std::string name; float killRadius; float damageFactor; public: Weapon(); // init all fields Weapon(const Weapon &other); // copy constructor Weapon operator=(Weapon &other); // assignment friend bool operator==(const Weapon &w1, const Weapon &w2); // equals friend bool operator!=(const Weapon &w1, const Weapon &w2); // not equals // other methods ... };
Planning for Debugging • You WILL have to debug your game • This will be likely done using a log file • You will MOST LIKELY have a log file for your game • Any class might have to be printed as text • Therefore: • Provide an insertion operator which can print a textual version of the class for debugging purposes
Planning for Debugging class Weapon { private: std::string name; float killRadius; float damageFactor; public: Weapon(); // init all fields Weapon(const Weapon &other); // copy constructor Weapon operator=(Weapon &other); // assignment friend bool operator==(const Weapon &w1, const Weapon &w2); // equals friend bool operator!=(const Weapon &w1, const Weapon &w2); // not equals friend std::ostream& operator<<(std::ostream &os, const Weapon &w); // insertion // other methods ... };
That Sounds like Too Much Work • Building classes correctly at the start • Becomes automatic and you do not think about it • They are ready for use anytime anywhere • You do not have to go back and do it later
Destructors • A class needs a destructor if • It has open files which need to be closed • It has open network connections which need to be closed • It has pointers to other objects which need to be deleted • It accesses any shared resource which needs to be released • It alters global variables to count number of instances or some value that depends on the number instances of each class
Parameter Passing • In C & C++ • All parameters are passed by copying some value... • In pass-by-value, the value of the parameter is passed • In pass-by-reference, a pointer to the real value is copied • Use pass-by-value when • You are passing a simple int, float, etc. • Use pass-by-reference when • You are passing an object • Remember • Arrays are ALWAYS passed by reference automatically
Passing By Reference is Easier • A reference is • A pointer which cannot be modified • When you pass by pointer • You must remember to dereference the pointer within the body of the method • When you pass by reference • The references are generated automatically • You do not need to dereference them within the method body
Assigning Responsibility • Often, we are faced with the question • “Which class should have this attribute?” • The general answer is • The class who properly owns the attribute • The class to which the attribute belongs
Assigning Responsibility • Consider a game board with objects on it • Should the position of the objects be stored • As an attribute of the objects ? • As an attribute of the game board ? • Consider the operations we will perform • Render the game board & objects • Move an object from one location to another • Determine which object was clicked
Analysis of Option1 & 2 • Option 1 (position in Board) • Pros • Cons • Moving requires looking up the current location of the pice on the board, which will be an expensive operation. • You tell the board to move a piece rather than telling the piece to move itself. This is not natural. • Option 2 (position in piece) • Pros • You tell piece to move itself and this is done efficiently and naturally. • It is easy to do hit detection and drawing
Analysis of Option1 & 2 • Option 2 is • slightly easier to implement • More natural and intuitive to use • More efficient • Why? • The position of a piece is a property of the piece, not of the board • The board should simply have a collection of pieces • In this case, the board uses a 2D array as a fast index into the pieces
RTTI • Run Time Type Identification • Requires classes to have at least 1 virtual function • Adds to the memory requirements • Can slow the program • Few programs must use RTTI • Proper class design and use of polymorphism can eliminate the need for RTTI • Remember: RTTI was a late addition to C++
Virtual Destructors • Destructors, like other methods, can be declared virtual • Consider: • class Parent {private:int *intArray;public: ~Parent() { if(intArray) delete [] intArray; }};
Virtual Destructors • class Child: public Parent {private:char *charArray;public:~Child() {if(charArray) delete [] charArray; }}; • Parent *p = new Child(); • delete(p); // invokes parent destructor! • The problem is that the wrong destructor is used
Virtual Destructors • This problem can be fixed by declaring the destructor virtual • class Parent {private:int *intArray;public:virtual ~Parent() { if(intArray) delete [] intArray; }};