refactoring self-documenting code and functional testing topics: beginning program bog lesson one...
TRANSCRIPT
RefactoringSelf-Documenting Code and Functional Testing
Topics:
Beginning Program Bog
Lesson One
Documentation
Comments
Refactored Code
RefactoringSelf-Documenting Code and Functional Testing
import java.awt.*;import java.awt.event.*;import java.awt.image.*;import java.net.*;import java.applet.*;
/** * A Simple TicTacToe applet. * A Tic Tac Toe applet. * A very simple, and mostly brain-dead * implementation of your favorite game! */
RefactoringSelf-Documenting Code and Functional Testing
/** * * In this game a position is represented by a white and black * bitmask. A bit is set if a position is occupied. There are * 9 squares so there are 1<<9 possible positions for each * side. An array of 1<<9 Booleans is created, it marks * all the winning positions. * * @version 1.2, 13 Oct 1995 * @author Arthur van Hoff * @modified 96/04/23 Jim Hagen : winning sounds * @modified 03/07/21 Sara Stoecklin : updated java version */
RefactoringSelf-Documenting Code and Functional Testing
publicclass TTTV0 extends Applet implements MouseListener { /** * White's current position. The computer is white. */ int white;
/** * Black's current position. The user is black. */ int black;
RefactoringSelf-Documenting Code and Functional Testing
/** * The squares in order of importance... */ final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7};
/** * The winning positions. */ static boolean won[] = new boolean[1 << 9]; static final int DONE = (1 << 9) - 1; static final int OK = 0; static final int WIN = 1; static final int LOSE = 2; static final int STALEMATE = 3;
RefactoringSelf-Documenting Code and Functional Testing
/** * Mark all positions with these bits set as winning. */ static void isWon(int pos) {
for (int i = 0 ; i < DONE ; i++) { if ((i & pos) == pos) {
won[i] = true; }}
}
RefactoringSelf-Documenting Code and Functional Testing
/** * Initialize all winning positions. */ static {
isWon((1 << 0) | (1 << 1) | (1 << 2));isWon((1 << 3) | (1 << 4) | (1 << 5));isWon((1 << 6) | (1 << 7) | (1 << 8));isWon((1 << 0) | (1 << 3) | (1 << 6));isWon((1 << 1) | (1 << 4) | (1 << 7));isWon((1 << 2) | (1 << 5) | (1 << 8));isWon((1 << 0) | (1 << 4) | (1 << 8));isWon((1 << 2) | (1 << 4) | (1 << 6));
}
RefactoringSelf-Documenting Code and Functional Testing
/** * Compute the best move for white. * @return the square to take */ int bestMove(int white, int black) {
int bestmove = -1;
RefactoringSelf-Documenting Code and Functional Testing
loop:for (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) &&
((black & (1 << mw)) == 0)) {int pw = white | (1 << mw);if (won[pw]) { // white wins, take it! return mw;}
RefactoringSelf-Documenting Code and Functional Testing
loop:…….
for (int mb = 0 ; mb < 9 ; mb++) { if (((pw & (1 << mb)) == 0) &&
((black & (1 << mb)) == 0)) {int pb = black | (1 << mb);if (won[pb]) { // black wins, take another continue loop;}
}}
RefactoringSelf-Documenting Code and Functional Testing
loop:…………..
// Neither white nor black can win in one move, this will do.if (bestmove == -1) { bestmove = mw;}
}}if (bestmove != -1) { return bestmove;}
RefactoringSelf-Documenting Code and Functional Testing
loop: ……
…………
// No move is totally satisfactory, try the first one that is openfor (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) &&
((black & (1 << mw)) == 0)) {return mw;
}}
// No more movesreturn -1;
}
RefactoringSelf-Documenting Code and Functional Testing
/** * User move. * @return true if legal */ boolean yourMove(int m) {
if ((m < 0) || (m > 8)) { return false;}if (((black | white) & (1 << m)) != 0) { return false;}black |= 1 << m;return true;
}
RefactoringSelf-Documenting Code and Functional Testing
/** * Computer move. * @return true if legal */ boolean myMove() {
if ((black | white) == DONE) { return false;}int best = bestMove(white, black);white |= 1 << best;return true;
}
RefactoringSelf-Documenting Code and Functional Testing
/** * Figure what the status of the game is. */ int status() {
if (won[white]) { return WIN;}if (won[black]) { return LOSE;}if ((black | white) == DONE) { return STALEMATE;}return OK;
}
RefactoringSelf-Documenting Code and Functional Testing
/** * Who goes first in the next game? */ boolean first = true;
/** * The image for white. */ Image notImage;
/** * The image for black. */ Image crossImage;
RefactoringSelf-Documenting Code and Functional Testing
/** * Initialize the applet. Resize and load images. */ public void init() {
notImage = getImage(getCodeBase(), "oimage.gif");crossImage = getImage(getCodeBase(), "ximage.gif");
addMouseListener(this); }
public void destroy() { removeMouseListener(this); }
RefactoringSelf-Documenting Code and Functional Testing
/** * Paint it. */ public void paint(Graphics g) {
Dimension d = getSize();g.setColor(Color.black);int xoff = d.width / 3;int yoff = d.height / 3;g.drawLine(xoff, 0, xoff, d.height);g.drawLine(2*xoff, 0, 2*xoff, d.height);g.drawLine(0, yoff, d.width, yoff);g.drawLine(0, 2*yoff, d.width, 2*yoff);
RefactoringSelf-Documenting Code and Functional Testing
int i = 0;for (int r = 0 ; r < 3 ; r++) { for (int c = 0 ; c < 3 ; c++, i++) {
if ((white & (1 << i)) != 0) { g.drawImage(notImage, c*xoff + 1, r*yoff + 1, this);} else if ((black & (1 << i)) != 0) { g.drawImage(crossImage, c*xoff + 1, r*yoff + 1, this);}
}}
}
RefactoringSelf-Documenting Code and Functional Testing
/** * The user has clicked in the applet. Figure out where * and see if a legal move is possible. If it is a legal * move, respond with a legal move (if possible). */ public void mouseReleased(MouseEvent e) {
int x = e.getX();int y = e.getY();
RefactoringSelf-Documenting Code and Functional Testing
switch (status()) { case WIN: case LOSE: case STALEMATE: play(getCodeBase(), "audio/return.au"); white = black = 0; if (first) {
white |= 1 << (int)(Math.random() * 9); } first = !first; repaint(); return;}
RefactoringSelf-Documenting Code and Functional Testing
// Figure out the row/columnDimension d = getSize();int c = (x * 3) / d.width;int r = (y * 3) / d.height;if (yourMove(c + r * 3)) { repaint();
RefactoringSelf-Documenting Code and Functional Testing
switch (status()) { case WIN:
play(getCodeBase(), "audio/yahoo1.au");break;
case LOSE:play(getCodeBase(), "audio/yahoo2.au");break;
case STALEMATE:break;
RefactoringSelf-Documenting Code and Functional Testing
default:if (myMove()) { repaint(); switch (status()) { case WIN:
play(getCodeBase(), "audio/yahoo1.au");break;
case LOSE:play(getCodeBase(), "audio/yahoo2.au");break;
case STALEMATE:break;
default:play(getCodeBase(), "audio/ding.au");
}
RefactoringSelf-Documenting Code and Functional Testing
} else { play(getCodeBase(), "audio/beep.au");}
}} else { play(getCodeBase(), "audio/beep.au");}
}
RefactoringSelf-Documenting Code and Functional Testing
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";
}}
RefactoringSelf-Documenting Code and Functional Testing
Lesson One: Self-Documenting Code and Functional Testing
RefactoringSelf-Documenting Code and Functional Testing
Process1. Read, review and understand2. Format and comment3. Perform functional testing4. Perform documenting refactorings
RefactoringSelf-Documenting Code and Functional Testing
1. Read, review and understand
A. Read existing code Not part of refactoring but necessary.
B. Review it for understandabilityDo the variables have meaningful names?Are their enough commentsDo the methods have meaningful names?
RefactoringSelf-Documenting Code and Functional Testing
2. Format and comment
• A. Format the code according to supplied standardsFormat the code by some standard to aid in
readability
Comment code for understandingAfter reading the code add any needed
comments to increase understandability
RefactoringSelf-Documenting Code and Functional Testing
import java.awt.*;import java.awt.event.*;import java.awt.image.*;import java.net.*;import java.applet.*;
/** * A Simple TicTacToe applet. A Tic Tac Toe applet. * A very simple, and mostly brain-dead * implementation of your favorite game! */
My format- get rid of extra white space to show on screens
RefactoringSelf-Documenting Code and Functional Testing
/** * A bitmask is used for the two players denoting positions occupied. * Each bit represents a square on the board 0..8. * The bit is 0 if not occupied and 1 if occupied. * Winning is determined by comparing the bitmask with * an array of Booleans that marks all the winning positions. * @version 1.2, 13 Oct 1995 * @author Arthur van Hoff * @modified 96/04/23 Jim Hagen : winning sounds * @modified 03/07/21 Sara Stoecklin : updated java version */
Rewrite comments to make them clearer
RefactoringSelf-Documenting Code and Functional Testing
publicclass TTTV11 extends Applet implements MouseListener {
static final int DONE = (1 << 9) - 1; // sentinal for square loop static final int LOSE = 2; // status for user wins static final int OK = 0; // status for game continues static final int STALEMATE = 3; // status for a tie static final int WIN = 1; // status for computer wins
Place all class constants first in alphabetical order
RefactoringSelf-Documenting Code and Functional Testing
int black; // user bitmask denotes user squares occupied Image crossImage; // user image boolean first = true; // who goes first next game
// The squares in order of importance... positions 0..8 final static int moves[] = {4, 0, 2, 6, 8, 1, 3, 5, 7}; Image notImage; // computer image int white; // computer bitmask denotes squares occupied static boolean won[] = new boolean[1 << 9]; // winning states
Place all class variables next in alphabetical order
RefactoringSelf-Documenting Code and Functional Testing
static void isWon(int pos) { // mark winning squares as true win for (int i = 0 ; i < DONE ; i++) { if ((i & pos) == pos) {
won[i] = true; } // end if (i & pos)} // end for
} // end isWon
I move comments to save real estate
RefactoringSelf-Documenting Code and Functional Testing
static { // initialize winning squares by shifting a one n bits isWon((1 << 0) | (1 << 1) | (1 << 2)); // row one winisWon((1 << 3) | (1 << 4) | (1 << 5)); // row two winisWon((1 << 6) | (1 << 7) | (1 << 8)); // row three winisWon((1 << 0) | (1 << 3) | (1 << 6)); // col one winisWon((1 << 1) | (1 << 4) | (1 << 7)); // col two winisWon((1 << 2) | (1 << 5) | (1 << 8)); // col three winisWon((1 << 0) | (1 << 4) | (1 << 8)); // dia right winisWon((1 << 2) | (1 << 4) | (1 << 6)); // dia left win
} // end static
Document all possible statements
RefactoringSelf-Documenting Code and Functional Testing
3. Functional testing • Execute the program and identify functional tests
Identify some functional tests to assure that the program CONTINUES to work as you add refactorings to the code. Lack of good functional tests causes problems in subsequent refactorings so take your time on this tasks. Document Tests to assure completeness in testing.
Document as complete as possible the needed tests. Create a excel spread sheet that identifies and clearly documents the needed tests.
RefactoringSelf-Documenting Code and Functional Testing
Complete test 9*9 for user and 9*9 for computer = 162 Good Coverage test 9*3(win,loose,stale) for user and 1*3 for computer = 30 Sufficient test 3 for user + 1 for computer = 4
USER FIRST COMPUTER FIRST
First Move Subsequent First Move Subsequent
User Comp User Computer Comp
User Comp
User
0 4 8 2 2 4
6 1 0 1
7 User Wins 2 6
3 5
2 4 5 8 7 8 Tie
1 0 Comp Wins
added because cannot depend on hitting 2 every time
6 4 3 0 7 4 0 6
8 7 2 1
1 2 8 5
5 Tie 3 4 Tie
RefactoringSelf-Documenting Code and Functional Testing
4. Perform Refactorings
1. Rename Method2. Rename Constants
3. Rename Variables
RefactoringSelf-Documenting Code and Functional Testing
Summary:
The name of a method does not reveal its purpose
Change the name of the method.
Rename Method
RefactoringSelf-Documenting Code and Functional Testing
Rename Method:
Motivation:
Methods should be named in a way the communicates their intension. Think what the comment for the method would be and turn that comment into the name of the method.
RefactoringSelf-Documenting Code and Functional Testing
Rename Method:
Customer____________getinvcrelmt ()
Customer____________getInvoiceCreditLimit()
RefactoringSelf-Documenting Code and Functional Testing
Rename Method:
Mechanics:
Check if method signature is implemented by a super or sub class. If so perform these steps for each implementation.
Declare new method with new name and copy old body into new method.
Change body of old so it calls the new one.
Find references to old and change them to refer to new one.
Remove old method.
Compile and test.
RefactoringSelf-Documenting Code and Functional Testing
CONSTANTS
static final int CONTINUE = 0; // OLD OK status for game continuesstatic final int ENDINGSTATE=(1<< 9)-1; //OLD DONE 111 111 111
Change these two constants to make code clearer……
TEST BETWEEN CHANGES.
Rename constants, variables methods
RefactoringSelf-Documenting Code and Functional Testing
if (won[black]) { return LOSE; } if ((black | white) == DONE) { return STALEMATE; } return OK; BECOMES if (won[black]) { return LOSE; } if ((black | white) == DONE) { return STALEMATE; } return CONTINUE;
TEST BETWEEN CHANGES.
RefactoringSelf-Documenting Code and Functional Testing
VARIABLES
Image crossImage; // user image Image notImage; // computer image BECOMES computerImage = getImage(getCodeBase(), "oimage.gif"); userImage = getImage(getCodeBase(), "ximage.gif");
int white; // White's current position. The computer is white. int black; Black's current position. The user is black. BECOMES int userStatus; //user bitmask denotes user squares occupied OLD BLACK int computerStatus; //computer bitmask denotes squares occupied WHITE
Rename constants, variables methods
RefactoringSelf-Documenting Code and Functional Testing
if ((white & (1 << i)) != 0) { g.drawImage(notImage, c*xoff + 1, r*yoff + 1, this);} else if ((black & (1 << i)) != 0) {
g.drawImage(crossImage, c*xoff + 1, r*yoff + 1, this);
BECOMES
if ((computerStatus & (1 << i)) != 0) { // if computer square taken g.drawImage(computerImage, c*xoff + 1, r*yoff + 1, this);
} else if ((userStatus & (1 << i)) != 0) { // if user square taken g.drawImage(userImage, c*xoff + 1, r*yoff + 1, this);
RefactoringSelf-Documenting Code and Functional Testing
VARIABLES
int bestMove(int white, int black)BECOMES
int bestMove(int computerStatus,int userStatus) { //compute best move
int pw = white | (1 << mw);BECOMES
int pw = computerStatus | (1 << mw);
int pb = black | (1 << mb);BECOMES
int pb = userStatus | (1 << mb);
RefactoringSelf-Documenting Code and Functional Testing
white = black = 0; BECOMEScomputerStatus = userStatus = 0;
white |= 1 << (int)(Math.random() * 9); BECOMEScomputerStatus |= 1 << (int)(Math.random() * 9);
white |= 1 << best;BECOMEScomputerStatus |= 1 << best;
RefactoringSelf-Documenting Code and Functional Testing
int best = bestMove(white, black); BECOMESint best = bestMove(computerStatus, userStatus);
if (((black|white)&(1<< m))!= 0) black |= 1 << m; BECOMESif (((userStatus|computerStatus)&(1<< m))!= 0) userStatus |= 1 << m;
if (((white&(1<< mw))== 0) &&((black&(1<<mw))== 0)) BECOMESif (((computerStatus&(1<< mw))== 0) &&((userStatus&(1<<mw))== 0))
RefactoringSelf-Documenting Code and Functional Testing
if ((black | white) == DONE) BECOMES
if ((userStatus | computerStatus) == DONE)
if (won[white]) return WIN;BECOMES
if (won[computerStatus]) return WIN;
if (won[black]) return LOSE;BECOMES
if (won[userStatus]) return LOSE;
if ((black | white) == DONE) BECOMES
if ((userStatus | computerStatus) == DONE)
RefactoringSelf-Documenting Code and Functional Testing
MORE VARIABLES
static int moves[] = {4,0,2,6,8,1,3,5,7}; BECOMESstatic int mostStrategicMove[] = {4,0,2,6,8,1,3,5,7};
int mw = moves[i]; BECOMES int mw = mostStrategicMove[i];
Rename constants, variables methods
RefactoringSelf-Documenting Code and Functional Testing
won[] = new boolean[1 << 9]; BECOMESwinningState[] = new boolean[1 << 9]; // winning states of game
if (won[white]) BECOMES if (winningState[computerStatus])
if (won[black]) BECOMES if (winningState[userStatus])
if (won[pw]) BECOMES if (winningState[pw])
RefactoringSelf-Documenting Code and Functional Testing
METHODS
boolean myMove(int m) { boolean yourMove() { BECOMESboolean legalComputerMove() { boolean legalUserMove(int m) {
if (myMove()) if (yourMove(c + r * 3)) BECOMESif (legalComputerMove()) if (legalUserMove(c + r * 3))
Rename constants, variables methods
RefactoringSelf-Documenting Code and Functional Testing
OTHERSmw BECOMESint potentialComputerMove = mostStrategicMove[i];
pb BECOMESint potentialUserStatus = userStatus | (1 << j);
c and r BECOMES row and col
Rename constants, variables methods
RefactoringSelf-Documenting Code and Functional Testing
loop:for (int i = 0 ; i < 9 ; i++) { int mw = moves[i]; if (((white & (1 << mw)) == 0) && ((black & (1 << mw)) == 0)) { int pw = white | (1 << mw); if (won[pw]) {return mw; } // white wins, take it! for (int mb = 0 ; mb < 9 ; mb++) {
if (((pw & (1 << mb)) == 0) && ((black & (1 << mb)) == 0)) { int pb = black | (1 << mb); if (won[pb]) {continue loop; } // black wins, take another
} }
}
A big difference BEFORE
RefactoringSelf-Documenting Code and Functional Testing
loop: for (int i = 0 ; i < 9 ; i++) { // for square = 0 to < 9 int potentialComputerMove = mostStrategicMove[i]; if (((computerStatus & (1 << potentialComputerMove)) == 0) && ((userStatus & (1 << potentialComputerMove)) == 0)) { int potentialComputerStatus = computerStatus | (1 << potentialComputerMove); if (winningState[potentialComputerStatus]) { // computer wins return potentialComputerMove; } /// end if (not user taken ) && ( not computer taken )
A big difference AFTER
RefactoringSelf-Documenting Code and Functional Testing
// the computer did not find a winning move for (int j = 0 ; j < 9 ; j++) { // for square = 0 to < 9 if (((potentialComputerStatus & (1 << j)) == 0) && ((userStatus & (1 << j)) == 0)) { int potentialUserStatus = userStatus | (1 << j); if (winningState[potentialUserStatus]) { // user wins, take another continue loop; } // end if won } // end if &&} // end for
// found a move but user would win
A big difference
RefactoringSelf-Documenting Code and Functional Testing
// computer has not found a winning move if (bestmove == -1) { // neither can win, this move will do bestmove = potentialComputerMove; } // end if } // end if && } // end for
if (bestmove != -1) { // if no move found return the best one return bestmove; } // end if bestmove
A big difference
RefactoringSelf-Documenting Code and Functional Testing
// there is no winning or good move – take mostStrategic movefor (int i = 0 ; i < 9 ; i++) { // no great move, try first one open int firstAvailableComputerMove = mostStrategicMove[i]; if (((computerStatus&(1<< firstAvailableComputerMove))== 0) && (userStatus & (1 << firstAvailableComputerMove)) == 0)) {
return firstAvailableComputerMove; } // end if && } // end for
return -1; // return no more moves } // end best move
A big difference