Royal Military College of Canada

Department of Electrical and Computer Engineering

EE573 - Object Oriented Analysis and Design
Dr G.S. Knight

Lab 4

Class Generalization and Polymorphism

Objective

The objective of this lab is to familiarize you with the important class relationship called generalization (inheritance). You will work with the different kinds of generalization, including simple, multiple and repeated inheritance. You will also explore how polymorphism helps in writing simpler, more abstract code. 

Background

In file Lab4.zip you are provided with files that implement the character initialization functions for a basic medieval role playing game. These files represent modules that belong to two packages:

Package gamedriver

Package characters

Set up a lab 4 directory with the appropriate sub-directories to represent these packages and copy the .java files into the directories. Create a new Kawa project, create the appropriate folders within the project to represent the packages, and add the appropriate .java files to the folders. Use the Project->Classpath dialog from the Kawa main menu to add you lab 4 directory to the class path. Now you can build and run the project.

The generalization structure for this simple program is shown in Figure 1 below. Common to all characters in the game are the following attributes: (1) name, (2) strength, and (3) intelligence. Strength and intelligence are relative measures and have values in the range of 1 to 20. The higher the value, the stronger or more intelligent the character is.


 Figure 1

A Warrior character inherits from the GameCharacter class and adds another attribute called weaponType. Similarly, Wizard characters inherit the basic characteristics of any game character, but have an additional attribute called spellNumber, which determines how many spells the wizard can cast.

As you can see in the provided Java code, class Game has specific member functions to add and remove Warrior and Wizard characters. You will test the existing code and your modifications to the code by using the class  GameTest, which is a JUnit test class.


Procedure

Using Sub-typing Relationships and Polymorphism

The program given to you is inefficient since we have a separate array for the storage of warriors and wizards. This is because an array by definition has to hold things of the same type.  Also Game.AddToGame() and Game.RemoveFromGame() are overloaded for each of the two possible classes Game deals with. If we were to add more kinds of game characters (and we will) the program would swell with a large amount of similar and redundant code. Modify the program such that there is a single array of characters in Game (take advantage of the sub-typing present), along with a single version of AddToGame() and RemoveFromGame() that will work with any kind of character.  There should be only one argument passed to the constructor of Game, which is the maximum number of characters in the game.

Note: We could use a Vector object to do this; it exploits the fact that all objects are a sub-type of the class Object. But instead you are required to implement this kind of functionality yourself and the Game class will become a specialized kind of container that manages objects which are kinds of game characters.

Note:  You will test your modifications with the class GameTest. When you modify the constructor the the class Game it is nessesary to modify the call to the constructor in the method  GameTest.setUp().
 

Inheritance - Adding More Character Classes

Modify your program to add the classes as shown in Figure 2. You should not have to change your new Game class to do this. All the classes should redefine member function whoAmI() (so that the information about characters is accurately given (for example, for class Druid, "I am a druid named... "). Note that Wizard no longer directly inherits from GameCharacter.

Again, modify the class GameTest to test your modifications and new classes. Some of the neccessary modifications are already indicated with comments in the source for GameTest, and an example test for the new class Centaur is given.  Add similar tests for the classes Troll and Druid and modify the method GameTest.testWizardAttributes() to provide good tests for the new attributes of the class Wizard.

Note: You are doing OO design; ensure that you are using good priciples of encapsulation, interface abstraction, access controls, and documentation.


Character Hierarchy
Figure 2

Adding Interfaces

Java is unlike C++ in that it allows two different kinds of sub-typing relationships. We just looked at inheritance via the extendskeyword.  Java also allows sub-typing by specifying that a class of objects will provide a certain specified interface.  That is,  classes of objects will behave in the same way if they implement the same interface (again a sub-typing relationship).  We can specify a common interface much the same way we specify a class.  The keyword interface is used in place of the keyword class in the definition.  Method signatures and their return types are allowed to be specified for interfaces. Any state variables which are part of interfaces are static and final by default (i.e. they are constants that belong to the interface and are not part-of each object that implements the interface).  We specify that a class will provide an interface by using the keyword implements.  It is used in much the same way as extends.  Since objects implementing the same interface are of the same "type/kind" polymorphism works across interface hierarchies in the same way it works across inheritance hierarchies. Only single-inheritance is allowed for class inheritance using extends, but multiple inheritance is allowed for interface inheritance using implements.

Create two interfaces called HumanCharacter and AnimalCharacter. Human characters should have a property called alignment which can have the value HumanCharacter.GOOD or HumanCharacter.EVIL. Animal characters should have a property called status which can have the value AnimalCharacter.FRIEND or AnimalCharacter.FOE.  The interfaces should support get and set methods for reading/setting the alignment (status) value.

Warrior, Wizard, Druid shall be of type HumanCharacter, and Centaur and Troll shall be of type AnimalCharacter. Add a Werewolf character that is both an animal and a human character.

The whoAmI() methods of the characters should report there status and/or alignment and magical energy as appropriate.

When you have finished you modifications you will modify the JUnit GameTest class to test your modifications.


Questions

Question 1:  For the original code given with the lab, specify whether each of the methods called by the class GameTest is: overloaded, over-ridden, polymorphic. Repeat this same exersize again for your completed modified code.

Question 2: The alignment and status state variables for the interfaces HumanCharacter and AnimalCharacter must be handled differently in implementation than the name, strength, etc. of a GameCharacter. Explain the difference. Why is there a difference?

Question 3: The access control for name, strength, etc. of a GameCharacter is friendly. Why is this a good choice in this case?

Question 4: Is it a good choice for the attributes of the classes Warrior and Wizard to be private? What is the implication?

Question 5:  This lab exersize is quite small. It can be said that the interfaces (HumanCharacter and AnimalCharacter) required by the lab add complexity but do not help much. What might be some of the benefits that they would provide as the software grows?

Question 6: How does polymorphism help you write more compact and abstract code?
Question 7: Produce a UML class diagram for part 3. Show only the name of the classes in the class icons and the relationships between the classes.


Deliverables