RefactoringClasses
Lesson Six: Classes
RefactoringClasses
Class Decomposition is difficult. Several Techniques exist: Behavioral Driven Design[Kowal] Data Driven Design[Mellor] State Driven Design[Schlaer] Responsibility Driven Design[Wirths-Brock] Discovery Driven Design [Norththrop] Framework Driven Design etc.
RefactoringClasses
Learning objective – have “good” classes which are reusable and easily maintained.
RefactoringClasses
We will use an approach which depends on clustering of methods, variables, and constants reside defined by McFadden. It is a data driven approach.
RefactoringClasses
Kind TypeVariables and Constants METHODS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
init
de
stro
y
dra
wG
rid
dra
wIm
ag
es
squ
are
Occ
up
ied
pa
int
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
use
Re
lea
sed
const int NINE-SHIFTING-BITSconst int ENDINGSTATE r r rconst int LOSE r r rconst int CONTINUE r r rconst int STALEMATE r r rconst int WIN r r rconst int NUMBER_OF_COLUMNS r r rconst int NUMBER_OF_ROWS r r rconst int FIRST_CELL r r rconst int LAST_CELL r r r
?? ??
SOME RED FLAGSSOME CLUSTERS
CONSTANTS
RefactoringClasses
VARIABLES
Kind Type Variables and Constants METHODS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
init
de
stro
y
dra
wG
rid
dra
wIm
ag
es
squ
are
Occ
up
ied
pa
int
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
use
Re
lea
sed
var int computerStatus p p p p p g s s s,g gvar int userStatus p p p p p p g s p s,gvar boolean computerFirst g svar Image computerImage s gvar Image userImage s gvar arrayint winningState[] u r r r
?? OK ??
RefactoringClasses
METHODS
Kind Type Variables, Constants, Methods METHODS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
init
de
stro
y
dra
wG
rid
dra
wIm
ag
es
squ
are
Occ
up
ied
pa
int
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
use
Re
lea
sed
ge
tUse
rMo
veS
tatu
s
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r rmethod void initmethod void destroymethod void drawGrid rmethod void drawImages rmethod boolean squareOccupied rmethod void paintmethod void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r rmethod void moveComputer rmethod void mouseReleased
RefactoringClasses
It is clear we need a GUI class that is separate from the domain of the game. This is a standard factorization and part of an architectural pattern called MVC (model view controller). We should have done this already in the previous lecture.
So I copy my class into a class called Domain and extract out from the TTT class all but the GUI related statements.
RefactoringClasses
public class TTTV61 extends Applet implements MouseListener {
// CONSTANTS static final int NUMBER_OF_COLUMNS = 3; static final int NUMBER_OF_ROWS = 3; // VARIABLES private Domain myDomain; // THIS IS ADDED private Image userImage; // user image private Image computerImage; // computer image
These variables belong with the GUI class(es).
RefactoringClasses
// GETS AND SETS public Image getUserImage (){return userImage;} public Image getComputerImage () { return computerImage;} public void setUserImage (Image userImage) { this.userImage = userImage;} public void setComputerImage (Image computerImage) { this.computerImage = computerImage; }
These methods also belong with the GUI class(es).
RefactoringClasses
//METHODS
public void init() { // initialize applet, load images, add listener setComputerImage(getImage(getCodeBase(), "oimage.gif")); setUserImage(getImage(getCodeBase(), "ximage.gif")); addMouseListener(this); myDomain = new Domain (); // ADDED } // end init
public void destroy() { removeMouseListener(this); } // end destroy
This method belongs to the commanding GUI class.
RefactoringClasses
public void drawGrid(Graphics g, Dimension d, int xoff, int yoff){ g.drawLine(xoff, 0, xoff, d.height); // draw first horizontal line g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second horizontal line g.drawLine(0, yoff, d.width, yoff); // draw first verticle line g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second verticle line } // end drawGrid
This method also belongs with the GUI class(es).
RefactoringClasses
public void drawImages(Graphics g, Dimension d, int xoff, int yoff){ int i = 0; for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) { // draw computer and user images
for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) { int cs = myDomain.getComputerStatus();// THESE STATEMENTS CHANGED and TWO new methods in mydomain if (myDomain.computerOccupiesSquare (i)) {
g.drawImage(getComputerImage(), col*xoff + 1, row*yoff + 1, this); } else if (myDomain.userOccupiesSquare ( i)) {
g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, this); } // end if }// end for
} // end for } // end draw Images
Again this method belong with the GUI class(es).
RefactoringClasses
public void paint(Graphics g) { // paint the screen
Dimension d = getSize(); g.setColor(Color.black);
int xoff = d.width / NUMBER_OF_ROWS; // compute x offsets int yoff = d.height / NUMBER_OF_COLUMNS; // compute y offsets
drawGrid (g, d, xoff, yoff); drawImages (g, d, xoff, yoff); } // end paint
This method belongs to the commanding GUI class.
RefactoringClasses
public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; // determine the column int row = (y * NUMBER_OF_ROWS) / d.height; // determine the row
if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) { resetGame(); repaint(); return; } // end if
Here there are methods and statements that are domain in nature.
For example the gameOver has to do with the domain of the game not the GUI. We need to extract out some of these methods from the GUI..
RefactoringClasses
int canidateMove = col + row * 3; if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) { repaint(); int status = getUserMoveStatus (canidateMove);
if (status == CONTINUE) { if (legalComputerMove(getComputerStatus(), getUserStatus())) { repaint(); moveComputer(getUserStatus());
} else { play(getCodeBase(), "audio/beep.au"); } // end else
} else { postGameStatus (status); } // end else } else { // not legal user move play(getCodeBase(), "audio/beep.au"); }// end else
} // end mouseReleased
All of these statements are domain in nature.
They need to be extracted in a method so they can be extracted to another class – The domain class.
RefactoringClasses
public void mouseReleased(MouseEvent e) { // user clicked applet int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location Dimension d = getSize(); int col = (x * NUMBER_OF_COLUMNS) / d.width; // determine the column int row = (y * NUMBER_OF_ROWS) / d.height; // determine the row
// THIS CODE ADDED WITH ALL THE DOMAIN STATEMENTS EXTRACTED// NOTE: the this is passed to allow for repainting by the non applet myDomain.playGame(row,col, this); } // end mouseReleased
THUS, we extract the previous code into ONE method called playGame. Now mouseRelease is now only doing things associated with the GUI and calling a method for domain activity. No domain statements are included in the GUI, only calls to the DOMAIN class(es).
RefactoringClasses
public class TTTV61 extends Applet implements MouseListener { // CONSTANTS static final int NUMBER_OF_COLUMNS = 3; static final int NUMBER_OF_ROWS = 3; // VARIABLES private Domain myDomain; private Image userImage; // user image private Image computerImage; // computer image
// GETS AND SETS public Image getUserImage (){return userImage;} public Image getComputerImage () { return computerImage;} public void setUserImage (Image userImage) { this.userImage = userImage;} public void setComputerImage (Image computerImage) { this.computerImage = computerImage; }
Here is the GUI class after all methods have been extracted that belong to the DOMAIN.
I HAVE TO ADD a variable for the domain class DOMAIN.
RefactoringClasses
//METHODS
public void init() { // initialize applet, load images, add listener setComputerImage(getImage(getCodeBase(), "oimage.gif")); setUserImage(getImage(getCodeBase(), "ximage.gif")); addMouseListener(this); myDomain = new Domain (); } // end init
public void destroy() { removeMouseListener(this); } // end destroy
WE keep the init and destroy methods in the GUI.
Here I make an instance of the new class called DOMAIN.
RefactoringClasses
public void drawGrid(Graphics g, Dimension d, int xoff, int yoff){ g.drawLine(xoff, 0, xoff, d.height); // draw first horizontal line g.drawLine(2*xoff, 0, 2*xoff, d.height); // draw second horizontal line g.drawLine(0, yoff, d.width, yoff); // draw first verticle line g.drawLine(0, 2*yoff, d.width, 2*yoff); // draw second verticle line } // end drawGrid
WE keep the drawGrid method in the GUI.
RefactoringClasses
public void drawImages(Graphics g, Dimension d, int xoff, int yoff){ int i = 0; // draw computer and user images for (int row = 0 ; row < NUMBER_OF_ROWS ; row++) {
for (int col = 0 ; col < NUMBER_OF_COLUMNS ; col++, i++) { int cs = myDomain.getComputerStatus(); if (myDomain.computerOccupiesSquare(i)) {
g.drawImage(getComputerImage(), col*xoff + 1, row*yoff + 1, this); } else if (myDomain.userOccupiesSquare(i)) {
g.drawImage(getUserImage(), col*xoff + 1, row*yoff + 1, this); } // end if }// end for
} // end for } // end draw Images
WE keep the drawImages method in the GUI.
Note the references to the DOMAIN class.
RefactoringClasses
public void paint(Graphics g) { // paint the screen
Dimension d = getSize(); g.setColor(Color.black);
int xoff = d.width / NUMBER_OF_ROWS; // compute x offsets int yoff = d.height / NUMBER_OF_COLUMNS; // compute y offsets
drawGrid (g, d, xoff, yoff); drawImages (g, d, xoff, yoff); } // end paint
WE keep the paint method in the GUI and it uses other GUI methods.
RefactoringClasses
public void mouseReleased(MouseEvent e) { // user clicked applet
int x = e.getX(); // get mouse x location int y = e.getY(); // get mouse y location
Dimension d = getSize();
int col = (x * NUMBER_OF_COLUMNS) / d.width; // determine the column int row = (y * NUMBER_OF_ROWS) / d.height; // determine the row
myDomain.playGame(row,col, this);
} // end mouseReleased
WE keep the mouseReleased method in the GUI.
It calls the playGame method in the DOMAIN class.
RefactoringClasses
public void mousePressed(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public String getAppletInfo() {return "TicTacToe by Arthur van Hoff"; }} // end TTT
WE keep the these methods in the GUI.
RefactoringClasses
Now we need to look at the domain class because it currently has a hodge podge of statements and methods that may need more class refactoring.
Next few slides show the resulting DOMAIN class.
RefactoringClasses
import java.applet.*;
public class Domain { // CONSTANTS static final int NINE_SHIFTING_BITS = 9; static final int ENDINGSTATE = (1 << NINE_SHIFTING_BITS) - 1; static final int LOSE = 2; // status for user wins static final int CONTINUE = 0; // OLD OK status for game continues static final int STALEMATE = 3; // status for a tie static final int WIN = 1; // status for computer wins static final int FIRST_CELL = 0; // first cell at row 1 col 1 static final int LAST_CELL = 8; // last cell at row 3, col 3
RefactoringClasses
// VARIABLES
private int computerStatus; ; // computer bitmask denotes squares occupied private boolean computerFirst = true; // who goes first next game private int userStatus; // user bitmask denotes user squares occupied private static boolean winningState[] = new boolean[1 << 9]; // winning states
private Applet myApplet;
WE need to declare the myApplet here so we can call repaint when we need to in the movment of the TTT tokens.
RefactoringClasses
private static void setWinningState(int winState) { // mark squares as true win for (int i = 0 ; i < ENDINGSTATE ; i++) { if ((i & winState) == winState) {winningState[i] = true; } } // end for } // end isWon static { // initialize winning squares by shifting a one n bits setWinningState((1 << 0) | (1 << 1) | (1 << 2)); // row one 000 000 111 setWinningState((1 << 3) | (1 << 4) | (1 << 5)); // row two 000 111 000 setWinningState((1 << 6) | (1 << 7) | (1 << 8)); // row three 111 000 000 setWinningState((1 << 0) | (1 << 3) | (1 << 6)); // col one 100 100 100 setWinningState((1 << 1) | (1 << 4) | (1 << 7)); // col two 010 010 010 setWinningState((1 << 2) | (1 << 5) | (1 << 8)); // col three 001 001 001 setWinningState((1 << 0) | (1 << 4) | (1 << 8)); // dia right 100 010 001 setWinningState((1 << 2) | (1 << 4) | (1 << 6)); // dia left 001 010 100 } // end static
RefactoringClasses
public int getComputerStatus () { return computerStatus; } public boolean getComputerFirst () { return computerFirst; } public int getUserStatus () { return userStatus; } public void setComputerStatus (int computerStatus) { this.computerStatus = computerStatus; } public void setComputerFirst (boolean computerFirst) { this.computerFirst = computerFirst; } public void setUserStatus (int userStatus) { this.userStatus = userStatus; }
public void setMyApplet (Applet myApplet) { this.myApplet = myApplet;}
WE need a setMyApplet method to set the class variable for use in many methods. Remember it was a parameter called by playGame in the GUI class.
RefactoringClasses
//METHODSpublic void Domain () { } NEW CONSTRUCTOR
public void playGame(int row, int col,Applet myApplet) { setMyApplet (myApplet); if (gameOver (gameStatus(getComputerStatus(), getUserStatus()))) { resetGame(); myApplet.repaint(); // NOTE REFERENCE return; } // end if
WE NEED a CONSTRUCTOR method.
Note the reference to myApplet.repaint which allows you to redraw the images after a move has been made.
RefactoringClasses
// playGame CONTINUED int canidateMove = col + row * 3;
if (legalUserMove(getComputerStatus(), getUserStatus(), canidateMove)) { makeMoves( canidateMove);
} else { // not legal user move myApplet.play(myApplet.getCodeBase(), "audio/beep.au"); }// end else } // end playGame
RefactoringClasses
public void continuePlaying ( ) { if (legalComputerMove(getComputerStatus(), getUserStatus())) { myApplet.repaint(); moveComputer(getUserStatus()); postGameStatus(gameStatus(getComputerStatus(), getUserStatus())); } else { myApplet.play(myApplet.getCodeBase(), "audio/beep.au"); } // end else } // end continueGame
public void makeMoves (int canidateMove) { myApplet.repaint(); moveUser(canidateMove); int status = gameStatus(getComputerStatus(), getUserStatus()); if (status == CONTINUE) { continuePlaying(); } else { postGameStatus (status); } // end else } // end makeMoves
NEW METHODS as a further refactoring to make methods functional.
RefactoringClasses
public boolean cellEmpty(int computerStatus,int potentialComputerMove,int userStatus) { return ((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0); } // end cellEmpty
public boolean userWins (int potentialComputerStatus, int userStatus) { for (int userMove = FIRST_CELL ; userMove <= LAST_CELL ; userMove++) { // for square = 0 to < 9 if (cellEmpty (potentialComputerStatus, userMove, userStatus)) { int potentialUserStatus = userStatus | (1 << userMove);
if (winningState[potentialUserStatus]) { // user wins, take another return true; } // end if won
} // end if cellEmpty } // end for return false; } // end checkIfUserWon
RefactoringClasses
public int bestMove(int computerStatus, int userStatus) { // compute best move int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};// square order of importance int bestMoveNotFound = -1; int bestmove = bestMoveNotFound;
RefactoringClasses
loop: for (int i = FIRST_CELL ; i <= LAST_CELL ; i++) { int potentialComputerMove = mostStrategicMove[i]; if (cellEmpty (computerStatus, potentialComputerMove, userStatus)) { int potentialComputerStatus = computerStatus | (1 << potentialComputerMove); if (winningState[potentialComputerStatus]) { // computer wins, take it!
return potentialComputerMove;} /// end if (not user taken ) && ( not computer taken )
if (userWins (potentialComputerStatus, userStatus)) { continue loop;} if (bestmove == bestMoveNotFound) { // neither can win, move will do bestmove = potentialComputerMove; } // end if } // end if && } // end for
RefactoringClasses
if (bestmove != bestMoveNotFound) { // if no move found return the best one return bestmove;
} // end if bestmove
// no great move, try first one open for (int anyMove = FIRST_CELL ; anyMove <= LAST_CELL ; anyMove++) { int firstAvailableComputerMove = mostStrategicMove[anyMove]; if (cellEmpty (computerStatus, firstAvailableComputerMove, userStatus)) { return firstAvailableComputerMove; } // end if && } // end for return bestMoveNotFound; // return no more moves } // end best move
RefactoringClasses
public boolean legalUserMove(int computerStatus, int userStatus, int canidateMove) { if ((canidateMove < FIRST_CELL) || (canidateMove > LAST_CELL)) { return false; } // end if if (squareOccupied (canidateMove, userStatus | computerStatus)) {
return false; } // end if return true; } // end legalUserMove
public boolean legalComputerMove(int computerStatus, int userStatus) { if ((userStatus | computerStatus) == ENDINGSTATE) { return false; } // end if return true;} // end legalComputerMove
RefactoringClasses
Public int gameStatus(int computerStatus, int userStatus ) { // if (win, loose, tie) if (winningState [computerStatus]) { return WIN; } // end if if (winningState [userStatus]) { return LOSE; } // end if if ((userStatus | computerStatus) == ENDINGSTATE) { return STALEMATE; } // end if return CONTINUE; } // end gameStatus public boolean squareOccupied (int i, int playerStatus) { return (( playerStatus & (1 << i)) != 0); } // end squareOccupied
RefactoringClasses
public void playComputerIfFirst ( ) { if (getComputerFirst()) {setComputerStatus ( 1 << (int)(Math.random() * 9)); }} // end playcomputerIfFirst
public void resetGame () { setComputerStatus(0); setUserStatus(0); playComputerIfFirst (); setComputerFirst (!getComputerFirst()); } // end resetGame
RefactoringClasses
public boolean gameOver(int status) { // check if over after computer move switch (status) {
case WIN:case LOSE:case STALEMATE:
return true; default:
return false; } // end switch } // end gameOver
RefactoringClasses
public void postGameStatus (int status) { switch (status) { case WIN: System.out.println ("I win ");
break; case LOSE: System.out.println ("You win "); break; case STALEMATE:
System.out.println ("No Winner "); break; default:
} // end switch } // end checkGameStatus
RefactoringClasses
public void moveComputer( int userStatus) { setComputerStatus (getComputerStatus() | 1 << bestMove(getComputerStatus(), userStatus)); } // end computerMove public void moveUser (int canidateMove) { setUserStatus (getUserStatus ( ) | 1 << canidateMove); } // end moveUser
RefactoringClasses
For EACH variable 1. Find the set method(s) for that VARIABLE. 2. Determine the SET of methods that use the set methods.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
bes
tMo
ve
leg
alU
serM
ove
leg
alC
om
pute
rMo
ve
gam
eS
tatu
s
pla
yCo
mpu
terI
fFirs
t
rese
tGa
me
gam
eO
ver
pos
tGa
meS
tatu
s
mo
veC
om
pu
ter
get
Use
rMo
veS
tatu
s
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
upie
d
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod int getUserMoveStatus rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean spaceOccupied r rmethod boolean continuePlayingmethod void constructor
var int computerStatus p p p p p s s s,g g g pi gvar int userStatus p p p p p p s p g,s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r rvar Applet myApplet p
RefactoringClasses
3. For each method in the SET (set method callers)4. Determine the variables referenced and set by each
method.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
bes
tMo
ve
leg
alU
serM
ove
leg
alC
om
pute
rMo
ve
gam
eS
tatu
s
pla
yCo
mpu
terI
fFirs
t
rese
tGa
me
gam
eO
ver
pos
tGa
meS
tatu
s
mo
veC
om
pu
ter
get
Use
rMo
veS
tatu
s
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
upie
d
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod int getUserMoveStatus rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean spaceOccupied r rmethod boolean continuePlayingmethod void constructor
var int computerStatus p p p p p s s s,g g g pi gvar int userStatus p p p p p p s p g,s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r rvar Applet myApplet p
RefactoringClasses
These X references are STRONG clusters. THUS, the STRONG Canidates for clustering are playComputerIfFirst and resetGame and moveComputer
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
bes
tMo
ve
leg
alU
serM
ove
leg
alC
om
pute
rMo
ve
gam
eS
tatu
s
pla
yCo
mpu
terI
fFirs
t
rese
tGa
me
gam
eO
ver
pos
tGa
meS
tatu
s
mo
veC
om
pu
ter
get
Use
rMo
veS
tatu
s
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
upie
d
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod int getUserMoveStatus rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean spaceOccupied r rmethod boolean continuePlayingmethod void constructor
var int computerStatus p p p p p s s s,g g g pi gvar int userStatus p p p p p p s p g,s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r rvar Applet myApplet p
RefactoringClasses
Now we look at the LOOSE clusterings. THUS the WEAK canidates for clustering are playGame and continuePlaying and bestMove.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
bes
tMo
ve
leg
alU
serM
ove
leg
alC
om
pute
rMo
ve
gam
eS
tatu
s
pla
yCo
mpu
terI
fFirs
t
rese
tGa
me
gam
eO
ver
pos
tGa
meS
tatu
s
mo
veC
om
pu
ter
get
Use
rMo
veS
tatu
s
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
upie
d
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod int getUserMoveStatus rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean spaceOccupied r rmethod boolean continuePlayingmethod void constructor
var int computerStatus p p p p p s s s,g g g pi gvar int userStatus p p p p p p s p g,s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r rvar Applet myApplet p
RefactoringClasses
We can determine ALL the STRONG methods, labeled in red.
computerStatus
userStatus
computerFirst
winningStatus
resetGame
playComputerIfFirst
moveComputer
resetGame
moveUser
resetGame
setWinningState
RefactoringClasses
Note that the resetGame method is Strong in with several class variables, so I will place it in the undecided box.
computerStatus
userStatus
computerFirst
winningStatus
resetGame
playComputerIfFirst
moveComputer
resetGame
moveUser
resetGame
setWinningState
Undecided
resetGame-S
RefactoringClasses
We can see that the two strong methods of computerStatus. The method playComputer references nonone but does a get on computerFirst. The method moveComputer references bestMove and does a get to computerStatus and userStatus .
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
Now we look at what these methods are referenced by. The Strong method playComputeIfFirst is referenced by resetGame. The Strong method moveComputer is referenced by continueplaying.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
The STRONG method playComputerFirst references noone. It is referenced by resetGame and gets the class variable computerFirst. We can see how these items begin to cluster together to form natural classes.
We show these sets, gets, references and parameters using a Rosetta-like map. A legend below shows how to read the map.
params
playGame
continuePlaying
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
bestMove
params
continuePlaying
Now we look at the next STRONG method moveComputer.
The method moveComputer references bestMove, gets computerStatus, and is referenced by continuePlaying.
playGame
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
bestMove
params
continuePlaying
Now we look at the next STRONG method moveComputer.
The method moveComputer references bestMove, gets computerStatus, and is referenced by continuePlaying.
playGame
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
The STRONG method playComputerFirst references noone. It is referenced by resetGame and gets the class variable computerFirst. We can see how these items begin to cluster together to form natural classes.
We show these sets, gets, references and parameters using a Rosetta-like map. A legend below shows how to read the map.
params
playGame
continuePlaying
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
bestMove
params
continuePlaying
Now we look at the next STRONG method moveComputer.
The method moveComputer references bestMove, gets computerStatus, and is referenced by continuePlaying.
playGame
RefactoringClasses
We then look at the Strong methods for userStatus and continue to trace its related references.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
bestMove
params
Now we look at the next STRONG method moveUser.
The method moveUser references nothing but is referenced by makeMoves. The methods continuePlaying and playGame perform gets on userStatus.
makeMoves
continuePlaying
playGame
RefactoringClasses
The third variable computerFirst has its only strong method resetGame in undecided. It does have a get from playComputerIfFirst but that has already been noted on the graph.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
The last variable winninStatus is updated (set) by the strong method setWinningStatus and setWinningStatus references nothing.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
set
getref
bestMove
params
STRONG methods for computerFirst and for winningStatus
The method setWinningStatus references nothing. But winningStatus does havereferences (gets) from userWins, bestMove, and gameStatus. We will do params later.
makeMoves
continuePlaying
playGame
userWins
gameStatus
RefactoringClasses
setgetrefparams
We will begin by defining variable clusters first.
It is clear that variable clusters computerStatus and userStatus are tightly coupled.
The winningStatus cluster could be moved out with or without bestMove.
The computerFirst cluster could be moved out or included with the computerStatus cluster.
computerStatus
userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
bestMove
makeMoves
continuePlaying
playGame
userWins
gameStatus
RefactoringClasses set
getrefparams
Now we need to group and expand this graph to see how the others cluster.
We begin by defining computerStatus/userStatus as one cluster and expand it out.
At this point we have the computerStatus/userStatus cluster with the methods moveUser and moveComputer as Strong and weak methods of continuePlaying, playGame, makeMoves, and bestMove.
computerStatus/userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-ScomputerFirst
bestMove makeMoves
continuePlaying
playGame
userWins
gameStatus
RefactoringClasses
Now we need to trace backwards from the weak methods to see their references. We first do it for the new cluster computerStatus/userStatus. Those include continuePlaying, playGame, and makeMoves.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
setgetrefparams
This tightens this clustering. The extended method makeMoves calls two of the weaker methods continuePlaying and playGame and also performs gets to both of the class variables. The extended methods are labeled in blue.
The methods continuePlaying, playGame and makeMoves are now classified as strong with the references to one another and the references to the class variables.
computerStatus/userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-S computerFirst
bestMove
makeMoves
continuePlaying
playGame
userWins
gameStatus
gameOver
postGameStatus
legalUserMove
legalComputerMove
RefactoringClasses
Before we decide to bring the strong methods into the cluster, we look at the methods that are also reference these strong methods. Since no more methods are referenced then we try them in the cluster.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
setgetrefparams
After we bring in the strong methods into the computerStatus/userStatus cluster, we can then look at the extended methods. This is not a one shot process. It make several attempts at clustering until you find the BEST solution since a perfect solution may not be possible. I usually save each version of clustering to allow regression.
The extended methods are legalComputerMove, legalUserMove, gameOver and postGameStatus
computerStatus/userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-S computerFirst
bestMove
makeMoves
continuePlaying
playGame
userWins
gameStatus
gameOver
postGameStatus
legalUserMove
legalComputerMove
RefactoringClasses
We look at these methods we note while the use the class variables of the current cluster as parameters or indirect parameters, they call one other method squareOccupied. Then we look at who references them and see those methods are already in the cluster.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
setgetrefparams
However, we show the linkages. These are the leaves of the linkages and they are targets for clustering with other clusters. It is difficult to know when you should stop and liik at the other potential clusterings. Possible scenarios are to do all the potential clusters and their weak methods then look at what might can be enclosed in a cluster.
We will stop here and look at what methods remain
computerStatus/userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-S computerFirst
bestMove
makeMoves
continuePlaying
playGame
userWins
gameStatus
gameOver
postGameStatus
legalUserMove
legalComputerMove
squareOccupied
RefactoringClasses
Now lets look at the methods that are included in the first cluster.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
Now lets look at the methods that are included in the first cluster.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
RefactoringClasses
setgetrefparams
Houston, we have clustering of two classes. The computerStatus/userStatus cluster now named PlayGame, will contain all the methods it takes to actually play the game. Another cluster named GameStrategy, will contain all the methods, regardless how simple, we are using to help the computer to win the game.
It only makes sense to put resetGame, and playComputerIfFirst in the Game class.
computerStatus/userStatus
winningStatus
playComputerIfFirst
moveComputer
moveUser
setWinningState
Undecided
resetGame-S
computerFirst
bestMove
makeMoves
continuePlaying
playGame
userWins
gameStatus
gameOver
postGameStatus
legalUserMove
legalComputerMove
squareOccupied
cellEmpty
userWins
RefactoringClasses
Now lets look at the methods that are included in the first cluster.
Kind Type Variables, Constants, Methods DOMAIN CLASS
setW
inn
ing
Sta
te
cellE
mp
ty
use
rWin
s
be
stM
ove
leg
alU
serM
ove
leg
alC
om
pu
terM
ove
ga
me
Sta
tus
pla
yCo
mp
ute
rIfF
irst
rese
tGa
me
ga
me
Ove
r
po
stG
am
eS
tatu
s
mo
veC
om
pu
ter
mo
veU
ser
ma
keM
ove
s
pla
yGa
me
squ
are
Occ
up
ied
con
tinu
eP
layi
ng
method void setWinningStatemethod boolean cellEmpty r rmethod boolean userWins rmethod int bestMove rmethod boolean legalUserMove rmethod boolean legalComputerMove rmethod int gameStatus r r
method void playComputerIffirst rmethod void resetGame rmethod boolean gameOver rmethod void postGameStatus r r
method void moveComputer rmethod void moveUser rmethod void makeMoves rmethod void playGamemethod boolean squareOccupied rmethod boolean continuePlaying rmethod void constructor
var int computerStatus p p p p p s s pi pi s,g g pi gvar int userStatus p p p p p p s pi pi p s g pi gvar boolean computerFirst g s,gvar arrayint winningState[] u r r r
`