refactoring self-documenting code and functional testing topics: beginning program bog lesson one...

60
Refactoring Self-Documenting Code and Functional Testing Topics: Beginning Program Bog Lesson One Documentation Comments Refactored Code

Upload: archibald-wilson

Post on 31-Dec-2015

225 views

Category:

Documents


0 download

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