LECTURE NOTES 3 DECEMBER 2004 ASSIGNMENTS 11:58 PM CodeLab #12 Fri 12/10 CodeLab #13 Wed 12/15 Assignment 5 Code INHERITANCE V. INTERFACE, CONT'D When? - to model a service or component relationship What? - don't use inheritance - do use *code reuse by composition* model the service class as a component Why? - clients can access inapplicable methods - dangerous b/c inapplicable - difficult to change b/c must continue to support, though not really part of model ASSIGNMENT 5 DISCUSSION DESIGN SOLUTION - Inheritance strategies - abstract Piece class, instantiable King and Pawn classes - Instantiable Piece class, inherited instantiable King class - must do this - Checkers and Checkerboard - separating the logical part of checkers from the display - Piece would not include draw methods - which one implements ClickListener? WindowPainter? - pros: can easily extend project to display differently: e.g., in the console - cons? - free to do either way CODING ISSUES - Window - purpose: to interface with the user - interactive features: - mouse clicks from the user - visual presentation ("painting") from inside the program - interfaces - each of these must be implemented by at least one class - can both be done by the same class - could be done by different classes - which of the class(es) that we discussed seem(s) like (a) good candidate(s)? - ClickListener - contents - 1 method: mouseClicked(int x, int y) - how do we say that a class implements this interface? - class MyClass implements ClickListener {...} - what does it mean that we implement this interface? - MyClass must contain a mouseClicked(int x, int y) method - what does it do? - very similar to KeyListener, but instead of observing keystrokes, observes? mouseclicks - trick question: when should we call this in our code? never! - if we don't call it, who does? - if an instance of a class that implements keyListener is passed to an instance of Window (in its constructor), then that class's implementation of KeyListener will automatically be called whenever the user clicks in that Window! - WindowPainter - contents - 1 method: redraw() - how do we say that a class implements this interface? - class MyClass implements WindowPainter {...} - what does it mean that we implement this interface? - MyClass must contain a redraw() method - what does it do? - tells a Window what it means to change it's appearance (repaint) - trick question: when should we call this in our code? never! - if we don't call it, who does? - if an instance of a class that implements WindowPainter is passed to an instance of Window (in its constructor), then that class's implementation of WindowPainter will automatically be called whenever the Window's repaint method is called - summary: - who calls repaint? our code - who calls redraw? Window.repaint - who implements repaint? Window - who implements redraw? our code - how to specify geometric shapes in space? - circle - rectangle - in Java, everything uses a bounding box - x, y upper left hand corner - width (x), height (y) - drawing - "Also note that you should not call drawCircle unless you are inside of the WindowPainter's redraw method. If you need to redraw the window manually, call repaint, which will in turn activate the WindowPainter's redraw method." - when do we want to redraw the window? - change the size of the screen - move a piece - change a piece from a regular piece to a king - jump a piece (piece deleted) - manually redrawing - when you encounter these events, your program has to tell the computer to "repaint" the screen to reflect the new data - due to implementation details, drawCircle and drawRectangle should only be called by WindowPainter.redraw() - who implements WindowPainter? - we do - this means that our code will have to call drawCircle and drawRectangle - note that though we implement it, we shouldn't call WindowPainter.redraw() - how is it ever called? - luckily, Window.repaint() calls WindowPainter.redraw() (which we write to call drawCircle and drawRectangle as appropriate) - so, whenever there's a change that affects the way the screen looks, we should call? Window.repaint - summary: - want to change the screen, call? Window.repaint - who implements Window.repaint? given - how does Window.repaint work? calls WindowPainter.redraw - who implements WindowPainter.redraw? us where? in our class that implements WindowPainter - how does redraw work? calls drawRectangle and drawCircle - questions? public class TicTacToe { private char[][] board; // Game board, filled with X, O, space private boolean isXTurn; // Is it X's turn? (X goes first) public static final int GAME_NOT_OVER = 0; public static final int GAME_CATS = 1; public static final int GAME_X_WON = 2; public static final int GAME_O_WON = 3; public TicTacToe() { reset(); } public void reset() { board = new char[][]{ {' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '} }; isXTurn = true; } public static boolean inBounds(int row, int col) { return (row >= 0 && row < 3 && col >= 0 && col < 3); } public char queryBoard(int row, int col) { if (!inBounds(row, col)) return ' '; return board[row][col]; } public void makeMove(int row, int col) { if (canMove(row, col)) { board[row][col] = isXTurn ? 'X' : 'O'; isXTurn = !isXTurn; } } public boolean canMove(int row, int col) { if (!inBounds(row, col) || gameStatus() != GAME_NOT_OVER) return false; return (board[row][col] == ' '); } private static int check3(char a, char b, char c) { if (a == b && b == c && a != ' ') { if (a == 'X') return GAME_X_WON; else return GAME_O_WON; } else return GAME_NOT_OVER; } public int gameStatus() { int status; // Check the rows for (int i = 0; i < 3; i++) { // Fixating on a certain row status = check3(board[i][0], board[i][1], board[i][2]); if (status != GAME_NOT_OVER) return status; // Fixating on a certain column status = check3(board[0][i], board[1][i], board[2][i]); if (status != GAME_NOT_OVER) return status; } // Should use check3 here as well // Upper-left to lower-right diagonal // Note: Both coordinates the same status = check3(board[0][0], board[1][1], board[2][2]); if (status != GAME_NOT_OVER) return status; // Upper-right to lower-left diagonal // Note: Both coordinates sum to 2 status = check3(board[0][2], board[1][1], board[2][0]); if (status != GAME_NOT_OVER) return status; // Check to see if board is full // We already know that if the board is not completely full, // the game is not over for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (board[i][j] == ' ') return GAME_NOT_OVER; return GAME_CATS; // Board full, nobody won } } import java.awt.Color; public class TicTacGraphics implements ClickListener, WindowPainter { private TicTacToe game; private Window win; public void mouseClicked(int x, int y) { int cellWidth = win.getWidth()/3; int cellHeight = win.getHeight()/3; game.makeMove(y/cellWidth, x/cellHeight); win.repaint(); if (game.gameStatus() != TicTacToe.GAME_NOT_OVER) { switch (game.gameStatus()) { case TicTacToe.GAME_CATS: System.out.println("Cat's game"); break; case TicTacToe.GAME_X_WON: System.out.println("Blue (X) won"); break; case TicTacToe.GAME_O_WON: System.out.println("Red (O) won"); break; } } } public void redraw() { int cellWidth = win.getWidth()/3; int cellHeight = win.getHeight()/3; if (cellWidth < 2 || cellHeight < 2) return; win.drawRectangle(0, 0, win.getWidth(), win.getHeight(), Color.white); win.drawRectangle(cellWidth-1, 0, 2, win.getHeight(), Color.black); win.drawRectangle(cellWidth*2-1, 0, 2, win.getHeight(), Color.black); win.drawRectangle(0, cellHeight-1, win.getWidth(), 2, Color.black); win.drawRectangle(0, cellHeight*2-1, win.getWidth(), 2, Color.black); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { switch (game.queryBoard(i, j)) { case 'X': win.drawCircle(cellWidth*j, cellHeight*i, cellWidth, cellHeight, Color.blue, false); break; case 'O': win.drawCircle(cellWidth*j, cellHeight*i, cellWidth, cellHeight, Color.red, false); } } } } public TicTacGraphics() { game = new TicTacToe(); win = new Window( (ClickListener)this, (WindowPainter)this, "Tic Tac Toe"); } public static void main(String[] args) { TicTacGraphics ttg = new TicTacGraphics(); } }