Title: Engr 691 Special Topics in Engineering Science Software Architecture Spring Semester 2004 Lecture Notes
1Engr 691Special Topics in Engineering Science
Software ArchitectureSpring Semester
2004Lecture Notes
2Solitaire Case Study (Budd's UOOPJ, Ch. 9)
- This is a set of slides to accompany chapter 9 of
- Timothy Budd's textbook
- Understanding Object-Oriented Programming with
Java, - Updated Edition
- (Addison-Wesley, 2000)
3The Solitaire Game
- Good example of inheritance in action
- Constructed from classes for cards, card piles,
and the game application
4Class Card
- import java.awt.
- public class Card
-
- // public constants for card dimensions
- public final static int width 50
- public final static int height 70
- // public constants for card suits
- public final static int heart 0
- public final static int spade 1
- public final static int diamond 2
- public final static int club 3
-
5Class Card (continued)
- // constructors
- public Card (int sv, int rv)
-
- s sv
- r rv
- faceup false
-
- // mutators
- public final void flip()
-
- faceup ! faceup
-
-
6Class Card (continued)
- // accessors
- public final int rank ()
-
- return r
-
- public final int suit()
-
- return s
-
- public final boolean faceUp()
-
- return faceup
-
7Class Card (continued)
- public final Color color()
-
- if (faceUp())
- if (suit() heart suit() diamond)
- return Color.red
- else
- return Color.black
- return Color.yellow
-
- public void draw (Graphics g, int x, int y)
/ ... / - // internal data fields
- private boolean faceup // card
face exposed? - private int r
// card rank - private int s
// card suit -
8Notes on Class Card
- Class implements playing card abstraction
- Textbook code rearranged into our standard format
-
- import makes package java.awt available to
programpart of Java API -
- Variables and classes can be used before defined
(except local variables in methods) -
9Notes on Class Card (continued)
- Java has no global variables, constants, or
enumerated data types -
- static data fields (i.e., class variables) used
for global - final data fields used for constants assigned
once, then not redefined - Three internal data fields
- rank and suit are read-only accessors but no
mutators - faceup can be modified flip() mutator
- draw is complex method accessor with side
effects - final methods accessors rank(), suit(),
faceup(), and color(), and mutator flip() - cannot be overridden in subclasses
- can be optimized expanded inline like a macro
efficient but inhibits reuse! - draw is not final implementation may be
somewhat platform dependent
10Card Images
- See Figure 9.3 on page 145 of Budd's
- UOOPJ.
11Method draw
- import java.awt.
- public class Card
-
- // ...
- public void draw (Graphics g, int x, int y)
-
- String names "A", "2", "3", "4",
"5", "6", "7", "8", "9", - "10", "J",
"Q", "K" - // clear rectangle, draw border
- g.clearRect(x, y, width, height)
- g.setColor(Color.blue)
- g.drawRect(x, y, width, height)
- // draw body of card
- g.setColor(color())
12Method draw (continued)
- if (faceUp())
-
- g.drawString(namesrank(), x3, y15)
- if (suit() heart)
- g.drawLine(x25, y30, x35, y20)
g.drawLine(x35, y20, x45, y30) - g.drawLine(x45, y30, x25, y60)
g.drawLine(x25, y60, x5, y30) - g.drawLine(x5, y30, x15, y20)
g.drawLine(x15, y20, x25, y30) - else if (suit() spade)
- g.drawLine(x25, y20, x40, y50)
g.drawLine(x40, y50, x10, y50) - g.drawLine(x10, y50, x25, y20)
g.drawLine(x23, y45, x20, y60) - g.drawLine(x20, y60, x30, y60)
g.drawLine(x30, y60, x27, y45) - else if (suit() diamond)
- g.drawLine(x25, y20, x40, y40)
g.drawLine(x40, y40, x25, y60) - g.drawLine(x25, y60, x10, y40)
g.drawLine(x10, y40, x25, y20) - else if (suit() club)
- g.drawOval(x20, y25, 10, 10)
g.drawOval(x25, y35, 10, 10) - g.drawOval(x15, y35, 10, 10)
g.drawLine(x23, y45, x20, y55) - g.drawLine(x20, y55, x30, y55)
g.drawLine(x30, y55, x27, y45) -
13Method draw (continued)
- else
- // face down
- g.drawLine(x15, y5, x15, y65)
- g.drawLine(x35, y5, x35, y65)
- g.drawLine(x5, y20, x45, y20)
- g.drawLine(x5, y35, x45, y35)
- g.drawLine(x5, y50, x45, y50)
-
-
14Notes on Method draw
- Displays the card on screen
-
- Parameter is current graphics context
-
- argument is object of class java.awt.Graphics
- class Graphics in Java API provides many drawing
primitives - methods for printing bit-maps available for more
sophisticated visuals - Constants such as Color.red and Color.black
defined in class java.awt.Color - draw is instance method every card responsible
for drawing itself
15The Game
- See Figure 9.4 on page 145 of Budd's UOOPJ.
16Class CardPile
- import java.awt.
- import java.util.Stack
- import java.util.EmptyStackException
- public class CardPile
-
- // constructors
- public CardPile (int xl, int yl)
-
- x xl
- y yl
- thePile new Stack()
-
17Class CardPile (continued)
- //mutators
- public final Card pop()
-
- try
- return (Card) thePile.pop()
- catch (EmptyStackException e)
- return null
-
-
- public void addCard (Card aCard) // sometimes
overridden -
- thePile.push(aCard)
-
- public void select (int tx, int ty) // sometimes
overridden - / do nothing /
18Class CardPile (continued)
- // accessors
- public final Card top()
- return (Card) thePile.peek()
- public final boolean isEmpty()
- return thePile.empty()
- public boolean includes (int tx, int ty) //
sometimes overridden -
- return x lt tx tx lt x Card.width
- y lt ty ty lt y Card.height
-
- public boolean canTake (Card aCard) // sometimes
overridden - return false
- public void display (Graphics g) // sometimes
overridden -
- g.setColor(Color.blue)
19Class CardPile (continued)
- // protected data fields
- // coordinates of the card pile
- protected int x
- // (x,y) is upper left corner
- protected int y
- // cards in the pile
- protected Stack thePile
-
20Notes on Class CardPile
- Implementation of card deck abstraction
- base class extended for specific behaviors
- Uses generic container Stack from Java API for
pile of cards - empty initially
- Stack operations implement Card operations
adapter pattern - Cast objects back to Card when taken from stack
- Uses global symbolic constants Card.width and
Card.height - access to static fields using class name, not
instance name (actually, either works) -
- pop, top, and isEmpty implementation for
subclasses -- final methods -
- addCard, select, includes, canTake, and display
part of abstraction, but implementation varies
among subclasses -
- protected data fields accessible by subclasses
-
- Instructor's comment
- protected data fields usually bad practice
trusts subclasses (perhaps in different package)
to manipulate internal fields correctly safer
to provide appropriate protected mutators and
accessors instead (perhaps final)
21Informal Specification of CardPile's Non-final
Methods
- Methods may need to be overridden to give the
card deck the appropriate behaviors. - addCard(c)
- adds card c to the card pile
- select(x,y)
- performs an action in response to mouse click at
(x,y) - includes(x,y)
- determines whether (x,y) within boundary of the
pile - canTake(c)
- determines whether the pile can take card c
(according to the rules governing the pile) - display(g)
- displays the pile using graphics context g
22Class SuitPile
- class SuitPile extends CardPile
- // constructors
- public SuitPile (int x, int y)
- super(x, y)
- // accessors
- public boolean canTake (Card aCard)
- // overrides parent
- if (isEmpty())
- return aCard.rank() 0
- Card topCard top()
- return (aCard.suit()
topCard.suit()) - (aCard.rank() 1
topCard.rank()) -
-
23Notes on Class SuitPile
- Represents one of the (4) piles of cards at top
of playing surface built up in suit from Ace
(1) to King (13) - Keyword extends indicates inheritance from
CardPile -
- super indicates parent in constructor used to
call parent's constructor for initialization
first statement only - Method canTake overridden by replacement
canTake if pile empty or card is next higher of
same suit
24Class DeckPile
- import java.util.Random
- class DeckPile extends CardPile
- // constructors
- public DeckPile (int x, int y)
- // first initialize parent
- super(x, y)
- // then create the new deck, first put
them into a local pile - for (int i 0 i lt 4 i)
- for (int j 0 j lt 12 j)
- addCard(new Card(i, j))
- // then shuffle the cards
- Random generator new Random()
- for (int i 0 i lt 52 i)
- int j Math.abs(generator.nextInt
() 52) - // swap the two card values
- Object temp thePile.elementAt(i)
- thePile.setElementAt(thePile.eleme
ntAt(j), i) - thePile.setElementAt(temp, j)
25Notes on Class DeckPile
- Represents the original deck of cards
- Uses super in constructor for basic
initialization, new code for specific
initialization - Accesses static (class) methods absolute value
function Math.abs() -
- Overrides method select by replacement if pile
nonempty, add top card to discard pile - Java API Stack extends Vector select uses the
"ranked sequence" methods of Vector - Instructor's comment
- Stack extending Vector is poor design in the Java
API! Similarly, the choice of Stack is a
questionable design. Better to just use Vector
directly
26Notes on Class DeckPile (continued)
- Accesses utility class java.util.Random to
generate pseudo-random numbers - Accesses static (class) variables to share single
copies - Solitaire.discardPile
- Author has only one discard pile, so made this
class access that instance directly - Instructor's comment
- Hard-coding of reference to static discard pile
inhibits reusability of the deck - Probably better to pass discard deck to
constructor of DeckPile, store reference,
manipulate it
27Notes on Class DeckPile(continued)
- Manipulates protected field thePile from parent
class CardPile - Possible alternatives to use of protected data
fields - Add (protected?) "ranked sequence" get and set
operations to CardPile abstraction? - Add constructor or (protected?) set operation to
CardPile that takes a sequence (vector, array,
etc.) of cards to (re)initialize pile? get
method? - Add shuffle operation to CardPile abstraction?
28Class DiscardPile
- import java.util.Random
- class DiscardPile extends CardPile
-
- // constructors
- public DiscardPile (int x, int y)
-
- super (x, y)
-
- // mutators
- public void addCard (Card aCard)
-
- if (! aCard.faceUp())
- aCard.flip()
- super.addCard(aCard)
-
29Class DiscardPile (continued)
- //mutators
- public void select (int tx, int ty)
-
- if (isEmpty())
- return
- Card topCard pop()
- for (int i 0 i lt 4 i)
- if (Solitaire.suitPilei.canTake(topCa
rd)) - Solitaire.suitPilei.addCard(top
Card) - return
-
- for (int i 0 i lt 7 i)
- if (Solitaire.tableaui.canTake(topCard
)) - Solitaire.tableaui.addCard(topC
ard) - return
-
- // nobody can use it, put it back on our
list - addCard(topCard)
-
30Notes on Class DiscardPile
- Represents the discard pile in a Solitaire game
- Constructor refines parent's constructor uses
super call in initialization - select method overrides and replaces one in
parent - checks whether topmost card can be played any
suit pile or tableau pile - addCard method overrides and refines one in
parent - executes parent method (super.addCard), but also
adds new behavior - flips card faceup when put on discard pile
- Hard-coded access to static variables for the (4)
suit piles and (7) tableau piles - probably better to pass these to constructor,
store references, and manipulate via local
references
31Class TablePile
- import java.util.Enumeration
- class TablePile extends CardPile
-
- // constructors
- public TablePile (int x, int y, int c)
- // initialize the parent class
- super(x, y)
- // then initialize our pile of cards
- for (int i 0 i lt c i)
- addCard(Solitaire.deckPile.pop())
-
- // flip topmost card face up
- top().flip()
-
32Class TablePile (continued)
- // mutators
- public void select (int tx, int ty)
- if (isEmpty()) return
- // if face down, then flip
- Card topCard top()
- if (! topCard.faceUp())
- topCard.flip()
- return
- // else see if any suit pile can take card
- topCard pop()
- for (int i 0 i lt 4 i)
- if (Solitaire.suitPilei.canTake(topCard))
- Solitaire.suitPilei.addCard(topCard
) - return
- // else see if any other table pile can
take card - for (int i 0 i lt 7 i)
- if (Solitaire.tableaui.canTake(topCard))
- Solitaire.tableaui.addCard(topCard)
- return
33Class TablePile (continued)
- // accessors
- public boolean canTake (Card aCard)
- if (isEmpty())
- return aCard.rank() 12
- Card topCard top()
- return (aCard.color() !
topCard.color()) - (aCard.rank()
topCard.rank() - 1) -
- public boolean includes (int tx, int ty)
- // don't test bottom of card
- return x lt tx tx lt x Card.width
y lt ty -
- public void display (Graphics g)
- int localy y
- for (Enumeration e thePile.elements()
e.hasMoreElements()) - Card aCard (Card)
e.nextElement() - aCard.draw (g, x, localy)
- localy 35
-
34Notes on Class TablePile
- Represents one of the (7) tableau piles on the
lower part of the playing surface - Initialized with cards from deck constructor
argument gives number needed - Top card is face up
- Add card to empty pile only if King
- Add card to nonempty pile only if opposite color
to top and one rank lower - Selection action moves any cards from this table
pile to suit pile or another table pile -
- Uses super in constructor for basic
initialization, new code for specific
initialization - Methods select, canTake, includes, and display
override and replace ones in parent - Method display
- Uses java.util.Enumeration object returned by the
Stack to iterate through the tableau pile - Displays each card slightly offset from the one
below it - Accesses protected field thePile from parent
class CardPile - Perhaps define (protected?) elements()
method in CardPile to return a pile-Enumeration
object to avoid protected fields - Another alternative method to return cards
in pile as sequence (e.g., array or vector)
35The Game Class Solitaire
- import java.awt.
- import java.awt.event.
- public class Solitaire
- // public class variables for the various
card piles of game - static public DeckPile deckPile
- static public DiscardPile discardPile
- static public TablePile tableau
- static public SuitPile suitPile
-
- // single array to alias all above piles --
aids polymorphism - static public CardPile allPiles
36The Game Class Solitaire (continued)
- // application entry point
- static public void main (String args)
-
- Solitaire world new Solitaire()
-
- // constructors
- public Solitaire ()
-
- window new SolitaireFrame()
- init()
- window.show()
37The Game Class Solitaire (continued)
- //mutators
- public void init ()
- // first allocate the arrays
- allPiles new CardPile13
- suitPile new SuitPile4
- tableau new TablePile7
- // then fill them in
- allPiles0 deckPile new DeckPile(335,
30) - allPiles1 discardPile new
DiscardPile(268, 30) - for (int i 0 i lt 4 i)
- allPiles2i suitPilei new
SuitPile(15 (Card.width10) i, 30) - for (int i 0 i lt 7 i)
- allPiles6i tableaui
- new TablePile(15
(Card.width5) i, Card.height 35, i1)
38The Game Class Solitaire (continued)
- // inner classes
- private class SolitaireFrame extends Frame
- / expanded later /
- // internal data fields
- private Frame window// the application
window
39Notes on Class Solitaire
- Class declares and allocates class (static)
variables for piles of cards on playing surface - Class sets up allpiles array to "alias" all other
piles, regardless of subclass - Control goes initially to class method main
- main creates an instance of Solitaire application
(its only action) - Solitaire constructor creates window for
application, initializes the playing surface, and
displays window - SolitaireFrame is an inner class
- inner class (from the glossary to Budd's
UOOPJ textbook) - a class definition that appears inside another
class. Inner classes are allowed access to both
the private data fields and the methods of the
surrounding class. Inner classes are frequently
used in building listener objects for handling
events. - SolitaireFrame manages the application window
40Inner Class SolitaireFrame
- // part of Class Solitaire
- private class SolitaireFrame extends Frame
-
- private class RestartButtonListener
implements ActionListener -
- public void actionPerformed
(ActionEvent e) -
- init()
- window.repaint()
-
-
-
41Inner Class SolitaireFrame(continued)
- private class MouseKeeper extends MouseAdapter
-
- public void mousePressed (MouseEvent e)
-
- int x e.getX()
- int y e.getY()
- for (int i 0 i lt 13 i)
- if (allPilesi.includes(x,
y)) -
- allPilesi.select(x,
y) - repaint()
-
-
-
-
42Inner Class SolitaireFrame(continued)
- public SolitaireFrame()
-
- setSize(600, 500)
- setTitle("Solitaire Game")
- addMouseListener (new MouseKeeper())
- Button restartButton new Button("New
Game") - restartButton.addActionListener(
- new RestartButtonListener())
- add("South", restartButton)
-
- public void paint(Graphics g)
-
- for (int i 0 i lt 13 i)
- allPilesi.display(g)
-
-
43Notes on Class SolitaireFrame
- GUI window for the Solitaire game application
- Extends java.awt.Frame class
- Event-driven program system GUI manager in
control, user code "called back" in response to
GUI events - SolitaireFrame constructor
- sets window size, title
- adds mouse listener "callback" code to respond to
mouse events - adds restart button at bottom of window ("South")
and "callback" code to respond to button events
44Notes on Class SolitaireFrame (continued)
- Inner class MouseKeeper's method mousePressed
called on mouse click event - determines which pile selected
- performs select operation for that pile
- note use of polymorphism
- action depends on pile type (i.e., on which
subclass) - repaints window
- Inner class RestartButtonListener method
actionPerformed called on button event - reinitializes game
- repaints window
- Method paint of SolitaireFrame called by GUI when
frame needs to be displayed - note use of polymorphism