69
241-211 OOP (Java): Design/7 241-211. OOP (Java) Objectives introduce coupling, cohesion, responsibility-driven design, and refactoring Semester 2, 2013-2014 7. Good Class Design

241-211 OOP (Java): Design/7 1 241-211. OOP (Java) Objectives – –introduce coupling, cohesion, responsibility-driven design, and refactoring Semester 2,

Embed Size (px)

Citation preview

241-211 OOP (Java): Design/7 1

241-211. OOP (Java)

• Objectives– introduce coupling, cohesion,

responsibility-driven design, and refactoring

Semester 2, 2013-2014

7. Good Class Design

241-211 OOP (Java): Design/7 2

Topics

• 1. Why Class Design?1. Why Class Design?

• 2. World of Zuul2. World of Zuul

• 3. Code Design Concepts3. Code Design Concepts

• 4. Code Duplication4. Code Duplication

• 5. Poor RDD5. Poor RDD

• 6. Ease of Modification6. Ease of Modification

• 7. Implicit Coupling7. Implicit Coupling

continued

241-211 OOP (Java): Design/7 3

• 8. Code Size?8. Code Size?

• 9. Refactoring Example9. Refactoring Example

• 10. Enumerated Types10. Enumerated Types

• 11. Using Enums in Zuul11. Using Enums in Zuul

• 12. Improved Zuul Class Diagrams12. Improved Zuul Class Diagrams

• 13. More Information13. More Information

241-211 OOP (Java): Design/7 4

1. Why Class Design?

• This part concentrates on how to create well-designed classes– getting code to work isn't enough

• Benefits of good design:– simplifies debugging, modification, maintenance– increases the chances of reusing code

241-211 OOP (Java): Design/7 5

2. World of Zuul

• We look at two versions of a simple We look at two versions of a simple adventure game called "World of Zuul" adventure game called "World of Zuul" (Zuul for short).(Zuul for short).

• Both versions have the same features, but Both versions have the same features, but the first is full of bad design choices, which the first is full of bad design choices, which I'll correct, leading to an improved second I'll correct, leading to an improved second version.version.

241-211 OOP (Java): Design/7 6

Zuul in Action My input follows the">>" zuul prompts.

241-211 OOP (Java): Design/7 7

Zuul Concepts

• Zuul is quite basic:Zuul is quite basic:– the user can move between a series of rooms, the user can move between a series of rooms,

get help, and quitget help, and quit

• A real adventure game would allow A real adventure game would allow multiple users, include hidden treasure, multiple users, include hidden treasure, secret passwords, death traps, and more.secret passwords, death traps, and more.

241-211 OOP (Java): Design/7 8

Zuul Map

outside theatrepub

lab office

rooms

exits/doors

The user starts in the"outside" room.The exits are to the

North, South, East, and West

241-211 OOP (Java): Design/7 9

Zuul Class Diagrams I can see someproblemsalready!

241-211 OOP (Java): Design/7 10

Class Descriptions

• ZuulGameZuulGame– creates the rooms, the parser, and starts the creates the rooms, the parser, and starts the

game. It evaluates and executes the commands game. It evaluates and executes the commands that the parser returns.that the parser returns.

• ParserParser– repeatedly reads a line from the terminal and repeatedly reads a line from the terminal and

interprets it as a two word command which it interprets it as a two word command which it returns as a Command objectreturns as a Command object

continued

241-211 OOP (Java): Design/7 11

• CommandWordsCommandWords– holds an array of all the command words in the holds an array of all the command words in the

game, which is used to recognise commandsgame, which is used to recognise commands• "go", "quit", "help""go", "quit", "help"

• RoomRoom– represents a room in the game, connected to represents a room in the game, connected to

other rooms via exits. The exits are labelled other rooms via exits. The exits are labelled "north", "east", "south", and "west"."north", "east", "south", and "west".

continued

241-211 OOP (Java): Design/7 12

• CommandCommand– holds information about a user command, holds information about a user command,

consisting of at most two stringsconsisting of at most two strings• e.g. "go" and "south"e.g. "go" and "south"

241-211 OOP (Java): Design/7 13

The First Adventure Game

• Colossal Cave AdventureColossal Cave Adventure (1976) was the (1976) was the first computer adventure game. first computer adventure game.

• It was designed by Will Crowther, a real-It was designed by Will Crowther, a real-life cave explorer, who based the game's life cave explorer, who based the game's layout on part of an actual US cave system.layout on part of an actual US cave system.

continued

241-211 OOP (Java): Design/7 14

• More info:More info:– http://www.rickadams.org/adventure/http://www.rickadams.org/adventure/– http://en.wikipedia.org/wiki/http://en.wikipedia.org/wiki/

Colossal_Cave_Adventure Colossal_Cave_Adventure

241-211 OOP (Java): Design/7 15

3. Code Design Concepts

• Coupling

• Cohesion

• Responsibility-driven Design (RDD)

• Refactoring

241-211 OOP (Java): Design/7 16

3.1. Coupling

• Coupling refers to the links between separate units (classes) in an application.

• If two classes depend closely on many details of each other, they are tightly coupled. Usually bad.

• Good design aims for loose coupling. Good.

241-211 OOP (Java): Design/7 17

Loose Coupling• In class diagrams, loose

coupling means less association lines

• Loose coupling makes it possible to:– understand one class without reading others– change one class without affecting others

• Loose coupling make debugging, maintenance, and modification easier.

241-211 OOP (Java): Design/7 18

3.2. Cohesion

• Cohesion is the mapping of tasks to code units (e.g. to methods and classes).

• We aim for high cohesion – good– each task maps to a single code unit

• a method should do one operation• a class should represent one entity/thing

continued

241-211 OOP (Java): Design/7 19

• High cohesion makes it easier to:– understand a class or method– use descriptive names– reuse classes or methods in other applications

241-211 OOP (Java): Design/7 20

3.3. Responsibility-driven Design (RDD)

• Each class should be responsible for manipulating and protecting its own data– e.g. don't use public fields

• RDD leads to low coupling, where code changes are localized– i.e. they only affect the class/method that is being

modified

241-211 OOP (Java): Design/7 21

3.4. Refactoring

• Refactoring is a two-stage redesigning of classes/methods when an application needs modifying or extending.

• Usually this leads to existing classes/methods being split up, and the addition of new classes/methods for the new features.

continued

241-211 OOP (Java): Design/7 22

Two Steps

• 1. Restructure the existing code, keeping the 1. Restructure the existing code, keeping the same same functionalityfunctionality, with very simple new , with very simple new classes/methods.classes/methods.– debug and test themdebug and test them

• 2. Add 2. Add new functionalitynew functionality to the classes/methods to the classes/methods created in step 1.created in step 1.– debug and test againdebug and test again

241-211 OOP (Java): Design/7 23

3.5. Thinking Ahead

• When designing a class, think what changes are likely in the future– aim to make those changes easier

• Example:– if the user interface is going to change (e.g.

text-based → GUI) then make sure all the IO is carried out by one class

241-211 OOP (Java): Design/7 24

3.6. Other Factors

• Coding style– commenting, naming, layout

• There's a big difference in the amount of work required to change poorly structured and well structured code.

241-211 OOP (Java): Design/7 25

4. Code Duplication

• Code duplication means that a single design change requires code changes in many places– makes maintenance harder

– e.g. printWelcome() and goDirection() in Room

continued

241-211 OOP (Java): Design/7 26

private void printWelcome()

{

System.out.println();

System.out.println("Welcome to the World of Zuul!");

System.out.println("Type 'help' if you need help.");

System.out.println();

System.out.println("You are " + currRoom.description);

System.out.print("Exits: ");

if (currRoom.northExit != null)

System.out.print("north ");

if (currRoom.eastExit != null)

System.out.print("east ");

if (currRoom.southExit != null)

System.out.print("south ");

if (currRoom.westExit != null)

System.out.print("west ");

System.out.println();

} // end of printWelcome()

continued

241-211 OOP (Java): Design/7 27

private void goDirection(Command command)

{

:

System.out.println("You are " + currRoom.getInfo());

System.out.print("Exits: ");

if (currRoom.northExit != null)

System.out.print("north ");

if (currRoom.eastExit != null)

System.out.print("east ");

if (currRoom.southExit != null)

System.out.print("south ");

if (currRoom.westExit != null)

System.out.print("west ");

System.out.println();

}

} // end of goDirection()

looksfamiliar

241-211 OOP (Java): Design/7 28

5. Poor RDD

• Room has a major design fault: public fieldsRoom has a major design fault: public fieldspublic String description;public Room northExit, southExit, eastExit, westExit;

• The Room implementation is exposed.The Room implementation is exposed.

• Instead, use private fields and get methodsInstead, use private fields and get methods– e.g. getExit()e.g. getExit()

continued

241-211 OOP (Java): Design/7 29

• Only Room should manage the room Only Room should manage the room desciption, but since it's a public field, desciption, but since it's a public field, ZuulGame can use it directly:ZuulGame can use it directly:– in ZuulGame.printWelcome():in ZuulGame.printWelcome():

System.out.println("You are " + currRoom.description);

• Make the description field private, and add Make the description field private, and add a get method (getInfo()).a get method (getInfo()).

241-211 OOP (Java): Design/7 30

6. Ease of Modification

• If adding a new, simple task to the design If adding a new, simple task to the design means a lot of extra coding, then it's an means a lot of extra coding, then it's an indication that the original design is not indication that the original design is not good.good.

• e.g. add new directions "up" and "down" to e.g. add new directions "up" and "down" to the "go" commandthe "go" command

continued

241-211 OOP (Java): Design/7 31

• This requires changes in:This requires changes in:– RoomRoom setExits()setExits()– ZuulGameZuulGame createRooms()createRooms()

printWelcome()printWelcome()goDirection()goDirection()

– room exits are manipulated in too many placesroom exits are manipulated in too many places

241-211 OOP (Java): Design/7 32

Why Limit the Exits?

• The Room design limits the exits to be The Room design limits the exits to be "north", "south", "east" and "west". Why?"north", "south", "east" and "west". Why?

• The Room class should allow any number The Room class should allow any number of exits, in any direction:of exits, in any direction:– use a HashMap to map a direction name (e.g. use a HashMap to map a direction name (e.g.

"up") to an adjacent Room object"up") to an adjacent Room object

continued

241-211 OOP (Java): Design/7 33

private HashMap<String, Room> adjRooms;

// maps directions to adjacent rooms

// in the Room constructor

adjRooms = new HashMap<String, Room>(); // no adjacent rooms initially

public void setAdjacentRoom(String dir, Room neighbour)

{ adjRooms.put(dir, neighbour); }

continued

241-211 OOP (Java): Design/7 34

• The limit of four directions in Room is visible outside The limit of four directions in Room is visible outside the class because of Room.setExits() used by the class because of Room.setExits() used by ZuulGame:ZuulGame:

Room outside = new Room("outside the main entrance");

Room theatre = new Room("in a lecture theatre");

Room pub = new Room("in the campus pub");

Room lab = new Room("in a computing lab");

Room office = new Room("in the admin office");

// link the room exits

outside.setExits(null, theatre, lab, pub);

theatre.setExits(null, null, null, outside);

pub.setExits(null, outside, null, null);

lab.setExits(outside, office, null, null);

office.setExits(null, null, null, lab);

continued

241-211 OOP (Java): Design/7 35

• Recode the interfaceRecode the interface– change setExits() to setAdjacentRoom(), which change setExits() to setAdjacentRoom(), which

sets one exit, and then call it as many times as sets one exit, and then call it as many times as neededneeded

Room outside = new Room("outside the main entrance");

Room theatre = new Room("in a lecture theatre");

Room pub = new Room("in the campus pub");

Room lab = new Room("in a computing lab");

Room office = new Room("in the admin office");

// link adjacent rooms

outside.setAdjacentRoom("east", theatre);

outside.setAdjacentRoom("south", lab);

outside.setAdjacentRoom("west", pub);

theatre.setAdjacentRoom("west", outside);

pub.setAdjacentRoom("east", outside);

lab.setAdjacentRoom("north", outside); :

241-211 OOP (Java): Design/7 36

7. Implicit Coupling• Coupling is when a class depends on data in Coupling is when a class depends on data in

another classanother class– e.g. ZuulGame uses Room.descriptione.g. ZuulGame uses Room.description– easy to see and fix since fields should easy to see and fix since fields should

be private in a good design (see slide 7)be private in a good design (see slide 7)

• Implicit coupling are Implicit coupling are links that are harder to seelinks that are harder to see– e.g. ZuulGame assumes a Room has at most 4 exits e.g. ZuulGame assumes a Room has at most 4 exits

when using setExits()when using setExits()

241-211 OOP (Java): Design/7 37

Example: Adding a new Command

• The current commands:The current commands:– go, help, quitgo, help, quit

• Add "look" to examine a room without going Add "look" to examine a room without going into it.into it.

• Requires changes to:Requires changes to:– CommandWordsCommandWords– ZuulGame: modify processCommand() and add a ZuulGame: modify processCommand() and add a

look() methodlook() method

continued

241-211 OOP (Java): Design/7 38

• in processCommand():in processCommand(): String cmdWord = cmd.getFirstWord();

if (cmdWord.equals("help"))

printHelp();

else if (cmdWord.equals("go"))

goDirection(cmd);

else if (cmdWord.equals("quit"))

isFinished = tryQuit(cmd);

else if (cmdWord.equals("look")) look();

// else ignore any other words

continued

241-211 OOP (Java): Design/7 39

• What about the output of "help":What about the output of "help":

• The "help" command is implicitly coupled to The "help" command is implicitly coupled to CommandWords, which means that "help" should use it CommandWords, which means that "help" should use it to list the commands.to list the commands.

continued

241-211 OOP (Java): Design/7 40

• Current version of printHelp():Current version of printHelp(): private void printHelp()

{

System.out.println("Please wander around at the university.");

System.out.println();

System.out.println("Your command words are:");

System.out.println(" go quit help");

}

implicit coupling toCommandWords

241-211 OOP (Java): Design/7 41

8. Code Size?

• Common questions:– how big should a class be?– how big should a method be?

• Answer in terms of method and class cohesion.

continued

241-211 OOP (Java): Design/7 42

• A method is probably too long if it does more then one logical task.

• A class is probably too complex if it represents more than one logical entity.

• Note: these are guidelines.

241-211 OOP (Java): Design/7 43

Method Cohesion Example

public void play()

{

printWelcome();

Command cmd;

boolean isFinished = false;

while (!isFinished) {

cmd = parser.getCommand(); // get a command

isFinished = processCommand(cmd); // process it

}

System.out.println("Thank you for playing. Goodbye.");

} // end of play()

241-211 OOP (Java): Design/7 44

Class Cohesion Example• How should items be added to the rooms?How should items be added to the rooms?

– an item has a description and weightan item has a description and weight

• Bad approach:Bad approach:– add description and weight fields to Roomadd description and weight fields to Room

• Good approach:Good approach:– create an Item class, and add a "collection of Items" create an Item class, and add a "collection of Items"

field to Roomfield to Room– better readability, extensibility, reuseabilitybetter readability, extensibility, reuseability

241-211 OOP (Java): Design/7 45

9. Refactoring Example

• How can multiple players be added to the How can multiple players be added to the game?game?– currently there is one player represented by the currently there is one player represented by the

current room he/she is occupyingcurrent room he/she is occupying– private Room currRoom; // in ZuulGameprivate Room currRoom; // in ZuulGame

• Based on RDD, players should be Based on RDD, players should be represented by objects of a new Player class.represented by objects of a new Player class.

241-211 OOP (Java): Design/7 46

Refactoring: Steps 1 and 2

• 1. Move 1. Move currRoomcurrRoom to a new Player class. to a new Player class. Test ZuulGame with one Player object.Test ZuulGame with one Player object.

• 2. Add the extra player fields to Player (e.g. 2. Add the extra player fields to Player (e.g. items, strength). Test ZuulGame with one, items, strength). Test ZuulGame with one, two, several Player objects.two, several Player objects.

241-211 OOP (Java): Design/7 47

10. Enumerated Types

• An enum type is a Java type made from a fixed set of constants.

• Common examples:– compass directions

(NORTH, SOUTH, EAST, and WEST)– the days of the week

(SUNDAY → SATURDAY)

241-211 OOP (Java): Design/7 48

• The bad C/C++ approach is to use "magic The bad C/C++ approach is to use "magic numbers" (e.g. 0, 1, 2, 3) instead of numbers" (e.g. 0, 1, 2, 3) instead of constants (e.g. NORTH , WEST)constants (e.g. NORTH , WEST)

• Putting the constants into a Java enum type, Putting the constants into a Java enum type, improves readability, adds extra features, improves readability, adds extra features, and allows more compile-time error and allows more compile-time error checking by javac.checking by javac.

241-211 OOP (Java): Design/7 49

10.1. The Day Enum Type

public enum Day

{

SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY

}

241-211 OOP (Java): Design/7 50

Using the Day Enumpublic class UseDay

{

public static void main(String[] args)

{

for (Day d : Day.values())

System.out.println(d); // d printed as a String

System.out.println();

Day d1 = Day.SUNDAY;

Day d2 = Day.TUESDAY;

if ( d1.compareTo(d2) < 0)

System.out.println(d1 + " is earlier in week");

else

System.out.println(d2 + " is earlier in week");

} // end of UseDay()

} // end of UseDay class

enum constants

241-211 OOP (Java): Design/7 51

Execution

241-211 OOP (Java): Design/7 52

Notes

• The enum type is a kind of Java classThe enum type is a kind of Java class– it inherits methods from the Enum classit inherits methods from the Enum class

• values(), compareTo(), etc.values(), compareTo(), etc.

– it's possible to add extra properties (fields) and it's possible to add extra properties (fields) and methods to an enum typemethods to an enum type• see the Flavour examplesee the Flavour example

241-211 OOP (Java): Design/7 53

10.2. The Flavour Enum Type

public enum Flavour

{

// 3 flavours with calorie property

CHOCOLATE(100),

VANILLA(120),

STRAWBERRY(80);

private int calories;

private Flavour(int cals) // must be private

{ calories = cals; }

public int getCalories()

{ return calories; }

} // end of Flavour enum

241-211 OOP (Java): Design/7 54

Creating Enum Constants

• The constructor for an enum type must be The constructor for an enum type must be private.private.

• The enum automatically creates the The enum automatically creates the constants defined at its beginningconstants defined at its beginning– you cannot invoke the enum constructor you cannot invoke the enum constructor

yourselfyourself

241-211 OOP (Java): Design/7 55

Using the Flavour Enumpublic class UseFlavour

{

public static void main(String[] args)

{ System.out.println("Known flavours");

for (Flavour f : Flavour.values())

System.out.println(f + " with calories " + f.getCalories() );

System.out.println();

Flavour f1 = Flavour.CHOCOLATE;

Flavour f2 = Flavour.STRAWBERRY;

if (f1.getCalories() < f2.getCalories())

System.out.println(f1 + " is lighter");

else

System.out.println(f2 + " is lighter");

} // end of UseFlavour()

} // end of UseFlavour class

241-211 OOP (Java): Design/7 56

Execution

241-211 OOP (Java): Design/7 57

11. Using Enums in Zuul

• Zuul should use an enum type for its Zuul should use an enum type for its command namescommand names– change "go", "quit", "help" into a CommandOp change "go", "quit", "help" into a CommandOp

enum type containing GO, QUIT, HELPenum type containing GO, QUIT, HELP

• Extra benefits are to include a String Extra benefits are to include a String property for the command names, and to property for the command names, and to add useful methods.add useful methods.

241-211 OOP (Java): Design/7 58

The CommandOp Enum Type

public enum CommandOp

{

// Command ops and their string versions

GO("go"), QUIT("quit"), HELP("help"), UNKNOWN("??");

private String cmdStr; // stores a command op's string version

private CommandOp(String commandStr)

{ cmdStr = commandStr; }

public String toString()

{ return cmdStr; }

continued

241-211 OOP (Java): Design/7 59

// ------------ static methods -------------

public static CommandOp getOp(String cmdName)

// return the CommandOp associated with a command name

{

for (CommandOp cmdOp : CommandOp.values())

if (cmdName.equals( cmdOp.toString() ))

return cmdOp;

return CommandOp.UNKNOWN; // more informative than returning null

} // end of getCommandOp()

continued

241-211 OOP (Java): Design/7 60

public static String listAll()

{

StringBuilder sb = new StringBuilder ();

for (CommandOp cmdOp : CommandOp.values())

if (cmdOp != CommandOp.UNKNOWN)

sb.append( cmdOp.toString() + " ");

return sb.toString();

} // end of listAll()

} // end of CommandOp class

a new String-related class, explained later

241-211 OOP (Java): Design/7 61

What are Static Methods?

• We've already met one use of static methodsWe've already met one use of static methods– main() (and other functions) use static so they main() (and other functions) use static so they

execute in the classexecute in the class

• The static methods in CommandOp execute The static methods in CommandOp execute in the enum typein the enum type– this makes sense since they utilize all the enum this makes sense since they utilize all the enum

constantsconstants

241-211 OOP (Java): Design/7 62

Uses of CommandOp

• In the Parser class:In the Parser class: // extract command and argument strings from the line String cmdStr = // get command as a string;

String argStr = // get argument string;

/* convert command and argument strings into

a Command object */

CommandOp cmdOp = CommandOp.getOp(cmdStr);

Command cmd = new Command( cmdOp, argStr);

static method call

241-211 OOP (Java): Design/7 63

• In ZuulGame:In ZuulGame:

private void printHelp()

{

System.out.println("Please wander around the unive.");

System.out.println();

System.out.println("Understood commands are:");

System.out.println( CommandOp.listAll() );

} // end of printHelp()

static method call

241-211 OOP (Java): Design/7 64

private boolean processCommand(Command cmd)

/* Execute a command. Return true if the command

finishes the game, false otherwise. */

{

boolean isFinished = false;

CommandOp cmdOp = cmd.getCommandOp();

if (cmdOp == CommandOp.UNKNOWN) {

System.out.println("Sorry, I don't know what you mean.");

return false;

}

else if (cmdOp == CommandOp.HELP)

printHelp();

else if (cmdOp == CommandOp.GO)

goDirection(cmd);

else if (cmdOp == CommandOp.QUIT)

isFinished = tryQuit(cmd);

return isFinished;

} // end of processCommand()

enum constants

241-211 OOP (Java): Design/7 65

private void printWelcome()

{

System.out.println();

System.out.println("Welcome to the World of Zuul!");

System.out.println("Type '" + CommandOp.HELP.toString() +

"' if you need help.");

System.out.println();

System.out.println( currRoom.getInfo() );

} // end of printWelcome()

241-211 OOP (Java): Design/7 66

StringBuilder

• A StringBuilder object is like a String, but can be A StringBuilder object is like a String, but can be modifiedmodified– its contents are changed in-place through calls such as its contents are changed in-place through calls such as

append(), without the overhead of creating a new object append(), without the overhead of creating a new object (as happens with String)(as happens with String)

• The The StringBufferStringBuffer class is similar to StringBuilder class is similar to StringBuilder but is slower since it can deal with Java threads.but is slower since it can deal with Java threads.

StringBuilder sb = new StringBuilder("Andrew");sb.append(" Davison");

241-211 OOP (Java): Design/7 67

12. Improved Zuul Class Diagrams

241-211 OOP (Java): Design/7 68

Notes

• The old CommandWords has been replaced The old CommandWords has been replaced by CommandOp.by CommandOp.

• CommandOp has simplified the code in CommandOp has simplified the code in ZuulGame, Command, and ParserZuulGame, Command, and Parser

• My UML tools cannot process enum typesMy UML tools cannot process enum types– I had to draw the CommandOp box myselfI had to draw the CommandOp box myself

241-211 OOP (Java): Design/7 69

13. More Information

• A great book on coding style (for any languA great book on coding style (for any language, not just Java):age, not just Java):– Code CompleteCode Complete

Steve McConnellSteve McConnellMicrosoft Press, Microsoft Press, 22nd ed., nd ed., 20042004

– the first edition is in the CoE librarythe first edition is in the CoE library