Experiments in Java


Session O1: Object-Oriented Design

You have learned that Java is an object-oriented language. You have also seen a number of reasons to use object-oriented languages. For example, objects provide a natural mechanism for modeling many problems and solutions. In addition, well-designed objects provide a natural mechanism for supporting reuse. But how does one do good object design? There is no real substitute for practice and experience: you learn object design by designing the objects for programs and then reflecting (and re-reflecting) on your designs. However, there are a number of processes by which beginning designers can develop the basic skills. We will investigate a few of those in this laboratory session.

The processes we will investigate include narratives, question and answer sessions, drawing diagrams, and generalization through related examples. You will find that it is often best to do design in dialog with others, as one person may find both flaws and additional possibilities in the design of another person.

Since design skills come from experience, it is important that you record your design decisions when you make them. Then, as you work on your project, you should reflect on those decisions and continue to record observations. If you have made a bad decision, it helps to have the implications documented so that you can avoid making similarly inappropriate decisions in the future. If you have made a good decision, it helps to have it documented so that you can reuse the ideas that led to that decision.

These strategies are certainly not the only ones you can use to improve your design skills. For example, you may find it helpful to study the design of larger systems. You might read about the design of such a system or even attempt to replicate that design.

Narratives

As an example, we will consider the game of Othello. In particular, we will think about how we might design a program that allows people to play Othello. If you do not know Othello, do not worry; here is a description:

Othello is a two-person game played on an eight-by-eight board. It is often convenient to number the rows of the board 1-8 and the columns A-H. The game begins with two black pieces and two white pieces on the center four squares of the board (D4, D5, E4, and E5). There is a black piece in the top-left and bottom-right of those four squares (D4 and E5). There is a white piece in the top-right and bottom-left of those four squares (D5 and E4). Players alternate turns, with each player making a legal move when it is his or her turn. A legal move consists of placing a piece so that two of pieces surround a row, column, or diagonal of the opponent's pieces. All of the opponent's pieces are then flipped (from black to white or from white to black). If a player cannot make a legal move, that player loses his or her turn. Play continues until there are no longer blank squares on the board or neither player can make a legal move. The player with the most pieces is the winner.

Note that this is, in effect, a short narrative describing the game. Such narrative descriptions are good first steps in object design. As with all pieces of prose, this narrative included both nouns and verbs. It is often the case that nouns correspond closely to the objects and classes in a program and that verbs correspond closely to the methods these objects and classes provide. If you circle the nouns, you are on your way to determining which objects and classes belong in your program. If you box the verbs, you are also on your way to determining the methods.

For example, you might note that the description uses the nouns player, piece, and move. This makes it likely that a program that simulates an Othello game will include the classes Player, Piece, and Move. What methods will these classes provide? Now we can turn our attention to the verbs. We see that players make moves and that pieces flip. This suggests that the Player class will most likely include a makeMove or chooseMove methods and that the Piece class will include a flip method.

Other nouns might suggest particular objects. For example, ``black'' is presumably an instance of Color, or at least the state of a Piece (or both). Still other nouns may be extraneous. For example, while the description of Othello uses the term ``opponent'', it is unlikely that we will need an Opponent class or even an opponent object in class Player. However, it is possible that some methods might benefit from an opponent variable.

In Experiment O1.1 you will begin to consider the design of an Othello class.

Question and answer sessions

The objects, classes, and methods one develops through narratives are often somewhat sketchy and are usually insufficient to form a whole program. We have not yet determined what attributes or fields each class needs, nor have we determined what parameters the methods will need. In addition, there are often a number of implicit objects in every description. How do we garner this additional information? We can garner information through a more careful reading of the narrative, asking questions as we go. What types of questions should we ask?

For example, closer reading of the description of Othello suggests that someone needs to determine whose turn it is. It may be helpful to use a Referee class that selects the current player. It may also be that the class that runs the game makes this determination.

Similarly, you note that there are many references to legal moves. What determines whether a move is legal? A set of Rules. The Rules also determine when the game is over and, in effect, who moves next.

Now let us consider the makeMove method. What information does a player need to make a move? The player certainly needs to know his or her color, the state of the board, and the applicable rules. Since a player always has the same color, we might make that a field in the Player class. On the other hand, the state of the board changes, so we might make that a parameter of the makeMove method. The rules are consistent, so we might also make those a field in the Player class.

Are these the only decisions we could have made? Certainly not. We might have instead chosen to make all color, board, and rules all parameters to the makeMove method or all fields in the Player class, or some other combination. How do you decide what is best to do? Again, experience will tell you. For now, a good strategy is to make things that can be changed by other objects (e.g., the board) parameters, while things that will remain consistent throughout the game (e.g., the rules) can be fields.

You might note that we have not begun to specify how makeMove operates, we have only specified what it does. At this point, it is appropriate to stay at this level of abstraction. In part, the how will be determined by our further steps. In part, the how may differ depending on other issues. For example, in a text-based Othello game, makeMove might draw the board and prompt for the player to type in a move; in a graphical Othello game, makeMove might wait for the player to click on a legal square; in a game against the computer, the computer player will make a move by running an algorithm that chooses an appropriate space.

Finally, we might ask ourselves what constitutes a move. Basically, it is the placement of a piece on the board at a particular position. This suggests that the Move class will have at least two attributes: a Piece and a Position. We also note that moves update the board. This suggests that the Board class will need an update method that takes a Move as a parameter.

In Experiment O1.2 you will refine your descriptions.

Beginning coding

At this stage in your design, you should have a rough outline of the classes you will need in the program, some of the methods those classes will need to provide, and some of the fields each class will use. You can use this information to start writing the code for your programs. For example, I've determined that each Player will need a Color for its pieces and a makeMove method. I might also note that I'll need the Color in order to create a particular player, giving me an idea of what the constructor will look like. This analysis leads to the following class description.


import Color;

/**
 * A person playing an Othello game (or, perhaps, any game).
 *
 * @author Samuel A. Rebelsky
 * @version 1.1 of September 1998
 */
public class Player {
  // +--------+--------------------------------------------------
  // | Fields |
  // +--------+

  /** The color of the pieces the player places on the board. */
  protected Color mycolor;


  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Create a new player that uses a particular color of
   * piece.
   */
  public Player(Color piececolor) {
    this.mycolor = piececolor;
  } // Player(Color)


  // +---------+-------------------------------------------------
  // | Methods |
  // +---------+

  /**
   * Choose a move to make (but don't make the move).
   */
  public Move makeMove(Board b) {
    // Code not yet available.
  } // makeMove(Board)

} // class Player


In Experiment O1.3 you will begin to develop classes for the project.

Narrating interactions

Our descriptions up to this point have ignored an important component: interaction with the user or users. While not all programs, classes, objects, or methods will interact with the user, many will. How do we determine what role such interactions play in the design of our program? We write more detailed narratives that describe how we envision users interacting with the program.

For example, we might describe two players using our Othello game as follows.

Two players, William and Jonathan, start the game. The game prompts for the player name. William enters his name for white; Jonathan enters his name for black. The game displays the game board (with white pieces at D5 and E4 and black pieces at D4 and E5). It then prompts William for his move. William places a white piece at C4, surrounding the black piece at D4. The game automatically flips the piece at D4 and then prompts Jonathan for his move. Jonathan chooses to place a black piece at A1. The game determines that this is not a legal move, informs Jonathan, and then asks for another move.

You'll note that we used ``the game'' a great deal in this narrative. You will often find that you use a similar noun (perhaps ``the program'') in describing interaction. Often, this will be your controlling object, or even the the main routine in that object.

How do we use such narratives? Sometimes it is to identify additional objects and classes (as in the Game class suggested in the previous paragraph). More frequently, it is to identify additional classes and methods that are necessary. For example, we might note that we need to draw boards, perhaps with a draw method provided by the Board class.

Again, it helps to ask questions. For example ``How does it display the game board?'' or ``How does it prompt for moves?'' You might also ask ``What object does the prompting?'' In this case, it is likely that the Player object prompts for moves as part of its work for the makeMove method. In order to do this, it may need additional parameters or fields, such as a SimpleOutput object to write to and a SimpleInput object to read from.

As we progress towards a full implementation, we can turn our narrative and questions into pseudocode, as in

To play Othello:
  create the board, rules, and players
  while the rules do not indicate that the game is over
    if the current player has an available move then
      repeatedly
        ask the current player to select a move
      until the player selects a legal move
      make that move, updating the board
      switch players
    else // the current player has no legal moves
      switch players
    end if
  end while
  count the pieces and report the winner

Thinking of alternative situations is also helpful. For example, one might describe an interaction between one player and the computer as follows.

Michelle starts the Othello game. It asks her whether she wants to play against the computer or another player. She selects the computer as an opponent. The game then asks for her name. She enters her name, Michelle. The game tells her that it will play white and she will play black. It then displays the board. The computer moves first and places a white piece at C4.

Again, we may want to ask some questions. For example, ``How does the computer make a move?'' One method would be to check every square on the board, identify all the legal moves, and pick one using some heuristic (a simple one is ``the first legal move I find''; a more interesting one might be ``the one that flips the most pieces''; better ones may pay attention to placement of pieces and number of pieces on the board).

Comparing alternate narratives can also help identify some objects. For example, if we read the second narrative again, we may ask ourselves ``Which object computes the computer's move?'' Since objects in class Player develop moves in the first scenario, it may make sense to use related objects in the second scenario. In particular, we might consider building a subclass of the Player object, such as ComputerPlayer.

In Experiment O1.4 you will develop narratives for some of the interactions in the game. In Experiment O1.5 you will continue coding the Othello game.

Diagrams of relationships

As you may have guessed, most object-oriented programs are not just collections of random objects, interacting chaotically. Rather, the interactions between objects are carefully scripted. How does one script these interactions? We can develop scripts by thinking carefully about the relationships between objects, again based on our earlier narratives.

The interaction narratives give a good beginning. For example, we can tell that:

Many programmers find it helpful to diagram these relationships, representing them visually in addition to or instead of textually. Often, it is best for each programmer to decide on the visualization that is most appropriate for him or her. Typically a diagram will include a box for each object in the program, with sub-boxes for each of the methods. A call to a method is represented by a line from calling object or method to callee. The response may be represented by a dotted line.

In Experiment O1.6 you will practice drawing diagrams.

Generalization and encapsulation

As we saw earlier, one of the primary reasons to do object-oriented programming is that it can easily support reuse. That is, well designed objects that work in one program should also be usable in another program. Similarly, by changing just one or two objects in a program, one might make it do something somewhat different.

In our Othello game, we might decide to support other size boards (e.g., 10x10 or 6x6), more than two players, or alternate starting positions. Each change may affect multiple classes. It is our goal to minimize the number of classes affected.

For example, if we had finished coding the Othello game and later decided to support different size boards (and we were not careful about the original design of the game), it is likely that we will need to change

If we design the various classes in such a way that they make fewer assumptions, these updates might be easier. For example, if we had written the makeMove method so that it queried the board for its size or for a list of legal spaces, then it might not be necessary to update makeMove at all when we changed the size of the board. The secret is to think about these alternatives before coding, rather than afterwards.

As another example, consider the pseudocode for a game of Othello that we developed earlier. Could we reuse this code for other games simply by changing the board, players, and rules? No. The pseudocode includes a number of assumptions about the rules of Othello. For example, it assumes that players alternate turns (except when one player cannot make a move). If we were going to support other games, such as chess, we would need to write a new description. Hence, it might be better to rewrite the algorithm as follows:

To play a game
  create the game board, rules, and players
  while the rules do not indicate that the game is over
    determine the current player
    let that player make a move
  end while
  determine the winner

In fact, we could even make this a parameterized method

To play a game given a board, rules, and players
  while the rules do not indicate that the game is over
    determine the current player
    let that player make a move
  end while
  determine the winner

In Experiment O1.7 you will consider other generalizations for the design of the Othello game.


Copyright (c) 1998 Samuel A. Rebelsky. All rights reserved.

Source text last modified Tue Oct 26 10:46:56 1999.

This page generated on Tue Oct 26 15:38:11 1999 by Siteweaver.

Contact our webmaster at rebelsky@math.grin.edu