23
Monopoly Use Case Text for this use case is very different than the NextGen POS problem, as it is a simple simulation. Game rules are captured in another document, rather than in the use case. Use Case UC1: Play Monopoly Game Scope: Monopoly application Level: user goal Primary Actor: Observer Stakeholders and Interests: - Observer: Wants to easily observe the output of the game simulation. Main Success Scenario: 1. Observer requests new game initialization, enters number of players. 2. Observer starts play. 3. System displays game trace for next player move Repeat step 3 until a winner or Observer cancels.

Use Case UC1: Play Monopoly Game - University of …webhome.cs.uvic.ca/~thomo/seng330/ch_18_20_monopoly.pdfMonopoly Use Case • Text for this use case is very different than the NextGen

  • Upload
    buidieu

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

Monopoly Use Case• Text for this use case is very different than the NextGen POS problem, as it is

a simple simulation. – Game rules are captured in another document, rather than in the

use case.

Use Case UC1: Play Monopoly GameScope: Monopoly applicationLevel: user goalPrimary Actor: ObserverStakeholders and Interests:

- Observer: Wants to easily observe the output of the game simulation.Main Success Scenario:1. Observer requests new game initialization, enters number of players.2. Observer starts play.3. System displays game trace for next player moveRepeat step 3 until a winner or Observer cancels.

SSD

We will focus on this one.

We didn't write a detailed use case or an operation contractas most people know the rules.Focus is the design issues, not the requirements.

Recall Domain Model

The Game-Loop Algorithm• First, some terminology:

turn -- a player rolling the dice and moving the pieceround -- all the players taking one turn

Now the game loop:

for N roundsplayRound()

playRound()for each Player p

p takes a turn

Assumption: Iteration-1 version does not have a winner, so the simulation simply runs for N rounds.

Which object should be Controller?

Who should be responsible for the game loop?

Who Takes a Turn?• Let’s apply Information Expert.

Information Needed Who Has the Information?current location of the player (to know the starting point of a move)

Taking inspiration from DM, a Piece knows its Square and a Player knows its Piece. Therefore, a Player software object could know its location.

the two Die objects (to roll them and calculate their total)

Taking inspiration from DM, MonopolyGame is a candidate since we think of the dice as being part of the game.

all the squares - the square organization (to be able to move to the correct new square)

Board is a good candidate.

Who Takes a Turn?• Three partial information experts for the "take a turn" responsibility:

– Player, MonopolyGame, and Board.• Why should be primary?• Guideline: When there are multiple partial information experts to choose from

place the responsibility in the dominant information expert-the object with majority of the information. This tends to best support Low Coupling.– Unfortunately, in this case, are all rather equal, each with about

one-third of information-no dominant expert.• Another guideline to try:

Guideline: When there are alternative design choices, consider the couplingand cohesion impact of each, and choose the best.– MonopolyGame is already doing some work, so giving it more

work impacts its cohesion, especially when contrasted with a Player an Board object, which are not doing anything yet.

– But we still have a two-way tie with these objects.

Who Takes a Turn?• Guideline: Consider probable future evolution of the software objects and the

impact in terms of Information Expert, cohesion, and coupling.

• For example, in iteration-1, taking a turn doesn't involve much information.

• However, consider the complete set of game rules in a later iteration. Then, taking a turn it’s not only about rolling the dice and moving the piece.

• Taking a turn can involve buying a property that the player lands on, if the player has enough money or if its color fits in with the player's "color strategy."

• What object would be expected to know a player's cash total?• Answer: a Player • What object would be expected to know a player's color strategy?• Answer: a Player

So, Player takes the turn

Taking a TurnTaking a turn means:1. calculating a random number total between 2 and 12

– (the range of two dice)2. calculating the new square location3. moving the player's piece from an old location to a new square location

• First, calculating a new random faceValue involves changing information in the Die, – so by Expert Die should be able to "roll" itself, and answer its faceValue.

• Second, the new square location problem: By LRG, it's reasonable that a Board knows all its Squares. – Then by Expert a Board will be responsible for knowing a new square

location, given an old square location, and some offset (the dice total).• Third, the piece movement problem: By LRG, it's reasonable for a Player to

know its Piece, and a Piece its Square location. – Then by Expert a Piece will set its new location.

Taking a Turn diagram(s)

Command-Query Separation Principle//style #1; used in the solutionpublic void roll() {

faceValue = //random num generation}public int getFaceValue() {

return faceValue;}

Why not make roll non-void and combine these two functions so that the rollmethod returns the new face Value, as follows?

//style #2; why is this poor?public int roll(){

faceValue = //random num generationreturn faceValue;

}

Command-Query Separation PrincipleThis principle states that every method should either be:• a command method that performs an action (updating,

coordinating, ..– often has side effects such as changing the state of objects, and is

void• a query that returns data to the caller and has no side effects

– it should not change the state of any objects

But, a method should not be both.• The roll method is a command - it has the side effect of

changing the state of the Die's faceValue. – Therefore, it should not also return the new faceValue, as then the

method also becomes a kind of query and violates the "must be void" rule.

Command-Query Separation Principle• Related to Principle of Least Surprise in software development.• What can happen when one violates CQS…

Missile m = new Missile();

String name = m.getName(); //looks harmless to me!

public class Missile {…public String getName() {

launch(); //launch missile?return name;

} //end of class

Class Squarepublic class Square {

private String name;private int index;

/** Creates a new instance of Square */public Square(String name, int index) {

this.name = name;this.index = index;

}

public String getName() {return name;}public int getIndex() {return index;}

}

Class Piecepublic class Piece {

Square location;

/** Creates a new instance of Piece */public Piece(Square location) {

this.location = location;}

public Square getLocation() {return location;}public void setLocation(Square location) {this.location = location;}

}

Class Diepublic class Die{

public static final int MAX = 6;private int faceValue;

public Die(){roll( );

}

public void roll() {face Value = (int) ( ( Math.random( ) * MAX ) + 1 );

}

public int getFaceValue() {return face Value;

}}

Class Boardpublic class Board {private static final int SIZE = 40;private List<Square> squares;/** Creates a new instance of Board */public Board() {

squares = new ArrayList<Square>(SIZE);buildSquares();

}

public Square getSquare(Square start, int distance) {int endIndex = ( start.getIndex() + distance ) % Board.SIZE;return squares.get(endIndex);

}

public Square getStartSquare() {return squares.get(0);

}

private void buildSquares() {for(int i=0; i<SIZE; i++) {

Square square = new Square("Square "+i, i);squares.add(square);

}}

}

Class Playerpublic class Player {private String name;private Piece piece;private Board board;private Die[] dice;

public Player(String name, Board board, Die[] dice) {this.setName(name);this.board = board;this.dice = dice; this.piece = new Piece(board.getStartSquare());

}

public void takeTurn() {System.out.println(name + " taking a turn...");

int rollTotal = 0;for(int i=0; i<dice.length; i++) {

dice[i].roll();rollTotal += dice[i].getFaceValue();

}

Square newLoc = board.getSquare(this.piece.getLocation(), rollTotal);

piece.setLocation(newLoc);

System.out.println(" new location is " + getLocation().getIndex());}

Class Player

public Square getLocation() {return this.piece.getLocation();

}

public String getName() {return name;

}

public void setName(String name) {this.name = name;

}}

Class MonopolyGamepublic class MonopolyGame {

private static final int ROUNDS_TOTAL = 20;private static final int PLAYERS_TOTAL = 2;private static final int DICE_TOTAL = 2;

private List<Player> players = new ArrayList<Player> (PLAYERS_TOTAL);private Board board = new Board();private Die[] dice = new Die [DICE_TOTAL];

public MonopolyGame() {for(int i=0; i<DICE_TOTAL; i++)

dice[i] = new Die();

for(int i=0; i<PLAYERS_TOTAL; i++) {Player player = new Player("Player " + i, board, dice);players.add(player);

}}

Class MonopolyGamepublic void playGame () {

for (int i=0; i<ROUNDS_TOTAL; i++)playRound ();

}

public List<Player> getPlayers() {return players;}

private void playRound() {for(Player player : players)

player.takeTurn();}

}