Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

Techniques of Computer Science: OOP with Java

The MVC Design Pattern

#1. First thing first: you really need to read this specification—all of it—with an intent to understand what this project is all about. I promise, the project isn't nearly so daunting as it specification may make it appear, but you must read this specification, not just look at the pictures! The semantics are in the text. A UML class diagram provides nearly enough information to create a class stub (some fields and their types, method signatures…) but there are practically no semantics conveyed by UML, other than those implied by relationships and naming conventions. Don't set yourself up to waste time later: read all of this!

#2. Second thing second: you really must

Program before you Code!

Really. If you can't program it, you can't code it! You need to draw pictures, explain things to others, handle a few simple physical models if it seems that doing so will be helpful. Regardless, you must understand what it is that you are trying to do; no amount of clever IDE syntax-directed assistance is going to morph a lack of semantic understanding into the solutions you need!

#3. Third thing third: I am planning to give you some blocks of code. Use those! My purpose is to save you some time  and  aggravation—lots  of  aggravation—by  providing  you  [at  least  fragments  of]  some  algorithmically complex methods  and/or methods that do  a lot  of graphical work. To that  end, it is  critical that your code conforms  to  the  class  names,  field  names  and  types,  and  method  signatures  as  they  are  detailed  in  the specification. If the specification includes an int field named count, its a pretty good bet that the code I provide won't compile correctly if you rename/retype count to something else. Furthermore, the code won't perform properly if count isn't semantically what the specification intends that it must be.

Got it? Good! Then let's get started.

As we discussed in class, the Model-View-Controller (MVC) Design Pattern is an architectural framework that prescribes how to arrange objects to realize the implementation of many common applications, from simple games to complex tools that help users painlessly interact with sophisticated data repositories. MVC describes a specific partitioning of roles and responsibilities across three categories of objects.

a Model is constructed from [one or more] objects that are responsible for maintaining the data on which the entire system revolves. The data is encapsulated by the Model objects. The Model provides methods that other components of the architecture (View(s) and Controller) use to access and update the data. But, the Model knows nothing about the presentation of the information it guards, not does the Model know much about the rules that govern if or when updates occur. Those tasks belong, respectively, to the View(s) and the Controller. One way to think about a Model is that it represents all of the information necessary to save/restore the state of the system (think of saving/recovering a video game, for example—that information).

In the context of this project, the Model will maintain the size of the game board, keep track of which player has the next turn, and for each position on the game board, which player (if any) occupies the position.

a View is a [usually] graphical view of some [or all] of the data managed by the Model, in a way that is meaningful/useful to the observer. Additionally, it is typical that the observer's interface with the MVC system is provided by a View (think of a product page on Amazon with its "click to buy" buttons).

For the project, you will create two views: one that depicts the game board and allows alternating players to make moves by clicking on unoccupied positions. Another view will depict the "score": the number of positions occupied by each player, as well as an indication of which is the current player.

a Controller is the heart of the  system, adjudicating moves and updating the Model data accordingly. The Controller encodes the algorithms (the "rules") of the system. By consolidating all decision-making at the Controller, the Model and View(s) need only to worry about faithfully representing—not interpreting—the data.

In order to implement the Controller for the Project, it would be a good idea to learn the rules. Take a look here for a discussion of Othello/Reversi on Wikipedia.

The figure above illustrates the cyclic flow of data and requests that are used by an MVC system. The steps that comprise a cycle are detailed below.

1.   The entire system is constructed and the Model, View(s) and Controller are "introduced" to one another.

The Model must be able to reference the View(s) in order to send the View(s) "update view" requests when the Model data changes.

The View(s) must be able to reference the Model in order to obtain current state information from the Model when the View(s) are initialized or need to update their presentations.

At least one of the View(s) must be able to reference the Controller in order to send the Controller notice of user interactions (game moves, if you like). If some View(s) do not support input from the observer, then those View(s) do not need to reference the Controller.

The Controller must be able to reference the Model in order to obtain current  state information when evaluating user interactions, and to make updates to the Model state consequent to user interactions.

2.   The  Model  sends  an  "update view" request to each the View(s). The View(s) respond by refreshing their presentations, using current data that is obtained by querying the Model.

3.   The entire MVC system becomes quiescent (quiet), awaiting the "Finger of God" to reach down and "make a move" at one the View(s).

4.   The  observer  [notably,  from  outside  the  MVC  system]  "makes  a  move"  by  clicking,  typing,  shouting, kicking…  whatever,  one  of  the  View(s).  The  View  sends  a  "move  proposed"  notice  to  the  Controller, including whatever information the View has about the move (eg., game board location).

5.   The Controller processes the "move proposal" by evaluating encoded "rules" within the context of the current Model data which the Controller obtains by querying the Model.

6.   Assuming that    the  "proposed" action is allowed, the Controller will appropriately request that the Model updates the state of the Model data.

7.   The Model updates the data as requested by the Controller.

8.   The entire MVC goes back to step 2—Wash, Rinse and Repeat.

The Model


Note: You will begin the Project by implementing the Model, integrating it with a "stub" Controller (which I shall provide) and testing it using commands entered from a keyboard to a "main" program. All of this can be done using  IntelliJ;  I strongly encourage  that  you  do  so!  At  this  point,  it  is  not  necessary  to use  the  graphical

capabilities available in Processing and Model will be the most challenging component to implement: you might as well make it easy[er].

Class Position

Position is an example of an "inner class", that is, a class that is defined as a member of a containing ("outer") class. The advantage of using an inner class (as opposed to simply creating an external class) is that the inner class is encapsulated within its containing outer class—nobody needs to know about it. Additionally, the methods of the inner class have access to all of the members of the containing class. Marking the inner class as a private member prevents access to the inner class from outside of the outer class' scope.

class Outer {

private void doThis() {

}

private class Inner {

public void doThat() {

doThis();   // accesses method of outer class

}

}

To be honest, using an inner class as we will in this project—to contain a single field, representing the Player currently occupying a location on the game board—is substantially overkill. But I wanted you to see and use an inner class at least once this term. [Originally, I had row and column fields also as members of the Position class, but as the solution had no need for the additional fields, I removed those from the specification. But, suppose you had a need to keep track of the number of times the Position changed hands during a game. It would be useful to add a field to Position such as

private int changes;

and perhaps an accessor method. But, we don't need those for this Project. Use the trivial Position as specified: you will gain at least a sense of their utility.]

Members of class Position

private Player occupiedBy indicates the Player currently occupying the game board location represented by this instance of Position. During construction, the field must be initialized to Player.NONE.

public Player getPlayer()

public void setPlayer(Player player)

are getter and setter methods for the property occupiedBy.

public String toString() should be conceptually familiar by now. The Position override of the method results in one of three possible strings, depending on the value of occupiedBy. If the Position has not been occupied, then the method returns ". " (dot followed by a space). Otherwise, the method returns either "B " or "W ", reflecting the current occupant of the Position.

Class Model

Model encapsulates all of the information that is necessary to record the state of a game of Othello: the size of the game board (the number of rows and columns, which, by the way, must be an even number greater than 2), the identity of the current player (represented as a value from the domain of the enum Player, and the state of occupancy of each of the positions on the game board. Only the use of a grid (2D array) of Position objects is specified; you are free to implement other necessary fields as you see fit. However, all of the specified methods must be implemented, regardless of the implementation of those properties.

Members of class Model

private Position[][] theBoard references a grid (a 2D array) of instances of class Position. Each element of the grid references an instance of information pertaining to a corresponding location on the game board;

A grid is simply an array of arrays:

private Position[][] theBoard;

This reference field must be initialized during construction (see also: method resetBoard()):

theBoard = new Position[numRows][numCols];

where numRows is the number of rows and numCols the number of columns comprising the grid. For the project, both numRows and numCols is size. Note that the all elements of the grid will be initially null! It will be necessary (using a pair of loops—see below) to initialize each element:

theBoard[row][col] = new Position();

By convention, we think of grids as being rows of columns and access its elements with nested loops; typically, the outer loop represents an iteration over rows while the inner loop iterates through the columns of a row:

for (int row = 0; row < numRows; row++)

for (int col = 0; col < numCols; col++)

… grid[row][col] …

[Java doesn't require that all rows of a grid have the same number of columns. Such non-uniform structures are called "ragged." For this reason, you will sometimes see the construct

for (int row = 0; row < grid.length; row++)

for (int col = 0; col < grid[row].length; col++)

… grid[row][col] …

where grid.length obtains the number of rows in the grid and grid[row].length is the number of columns in the specified row of the grid. And, of course, these concepts extend to 3D matrices (and beyond). We will not use ragged structures in this Project.]

A word  of  caution:  when  using  2D  grids  in  graphical  applications,  remember  that column numbers correspond to 'X' positions and row numbers to 'Y' positions. This gets very confusing since we are used to thinking about (x, y) coordinates, but we generally program using [row][col]. Be appropriately thoughtful!

[Note: While you have worked with lists of objects (Itinerary, for example), you have not done much with arrays of objects. They work just as you would assume:

theBoard[someRow][someColumn]  // value is a Position!

is a reference to a Position instance, so

(theBoard[someRow][someColumn]).getPlayer()  // parens optional!

does  exactly  what  you  [should]  expect:  it  is  the  identity  of  the  Player  occupying  the  game  board  at  the corresponding row and column. Don't make something hard when it isn't!]

public Model(int size) is a constructor method, where the positive, even size (greater than 2) is the number of rows and columns that form the square 2D grid, theBoard. The grid must be created and all elements of the grid must be initialized with instances of Position. The current turn's player is initialized to Player.BLUE and the four central members of theBoard are initialized as shown in the illustration of the game board at the top of this document. This last step can be performed by invoking the setOccupant (see below) method:

setOccupant(size/2-1, size/2-1, Player.BLUE);

setOccupant(size/2-1, size/2, Player.WHITE);

setOccupant(size/2, size/2-1, Player.WHITE);

setOccupant(size/2, size/2, Player.BLUE);

Implementation hint: most—not all—of the work performed by the constructor is the same work performed by the method resetBoard (see below). Consider invoking resetBoard as part of the implementation of the constructor! public String toString() produces a textual representation of the state of the game board. Each position of the grid is represented by a two character string: ". ", "W " or "B " (see Position toString() above).  For example the initial configuration of a size 6 board would result in the following when output:

.  .  .  .  .  .

.  .  .  .  .  .

. . B W . .

. . W B . .

.  .  .  .  .  .

.  .  .  .  .  .

public int getCount(Player player) returns the number  of game board  locations  occupied by the specified player. For example, if the current state of the game board, as related by toString, were:

.  .  .  .  .  .

.  .  .  .  .  .

. . B W . .

. . W W W .

. . . W B .

.  .  .  .  .  .

then, getCount(Player.BLUE) should return 2 and getCount(Player.WHITE) should return 5.

public int getSize()

public Player getCurrentPlayer()

public Player getOccupant(int row, int col)

public void   setOccupant(int row, int col, Player player)

public boolean isOccupied(int row, int col)

public boolean isOccupiedBy(int row, int col, Player player)

The getters, "isErs" and setters above should be self-describing. If you have questions, be sure to reach out for clarification before declaring your implementation finished!

Implementation Hint: isOccupiedBy can/should be used in the implementation of getCount.

public void resetModel() will reset the Model to it's initial configuration: all locations on the game board will be reset to Player.NONE (unoccupied) with the exception of the four central positions which will be set to their proper values (see the description of the Model constructor, above), and the current turn player will be set to Player.BLUE. Implementation Note: it would be reasonable to invoke resetModel from the Model constructor in order to eliminate duplicated code: resetModel does much of the same initialization that a constructor would do! Implementation Hint: Use methods in the implementation of other methods! Don't repeat work that has already been done elsewhere. Methods represent significant high-level components: use them! Not repeating work greatly reduces debugging efforts and enhances program readability and maintainability.

public void takeTurn() is invoked by the Controller as the Controller's final responsibility as it completes the processing of a successful move. The method must perform two tasks:

the current turn player is exchanged (from BLUE to WHITE, or from WHITE to BLUE); and

the external method, updateViews() is invoked.

The method updateViews() is in the enclosing class scope that includes both Model and Controller. Simply invoke the method. The updateViews method causes each View to update its presentation, based on the current state of the Model following the processed move.

Testing Model

Waiting until after integration of all of the MVC components to begin testing is generally not a good plan. The integrated system is moderately complex, making it difficult to trackdown errors—and there almost certainly will be some errors. Of course, some testing needs to wait until after integration, but that will go much easier if the individual components are "unit tested" as they are developed and partially integrated subsystems are tested as they are integrated. [Unit testing refers to testing individual methods and subsystems, being sure that they satisfy all specified requirements and particularly, testing boundaries.]

You should thoroughly test the Model "alone." That will require a small framework that "simulates" the behavior of the as-yet unwritten View(s) and Controller. Reviewing the MVC flow discussed pages ago, what does the test framework for Model need to accomplish?

Needed: in lieu of a View, a mechanism that "reveals" the state of the Model;

Needed: a way to exercise the Model's role in handling update requests that would be made by the Controller;

• Needed: a way to ensure that Model is properly managing the "next turn" protocol of changing the player and issuing "update view" requests.

Essentially, we need to write a small program that exercises a Model just as it would be in a fully integrated MVC. And, perhaps surprisingly, that is not difficult.

A Test Framework

Let's design a test framework for Model. It will need to provide Model with the same requests it would receive from a Controller responding to move proposals from a View. We can accomplish this by creating a "stub" Controller:

public class Controller {

private Model model;

public Controller (Model model) {

this.model = model;

}

public void evalMove(int row, int col) {

model.setOccupant(row, col, model.getCurrentPlayer());

model.takeTurn();

}

}

Notice that this stub has the same public interface—the same public methods—as would a full implementation of Controller: a constructor that requires (and saves) a reference to a Model, and an evalMove method that would be invoked from an as-yet unimplemented View. The evalMove method doesn't do any evaluation of game rules, but simply directs the Model to "occupy" the specified position on the game board with the current turn player. Then evalMove invokes the Model takeTurn method. The stubbed Controller does a reasonable job of testing the Model; by making good choices for row and column, you are able to check boundaries.

We will also have to provide a definition for the enum Player and "fake" the existence of an updateViews() method that Model can use to "refresh" the non-existent View(s). While no View will actually do anything, we are only interested in observing that Model does the right thing at the right time. We can verify that the Model actually does its work correctly using Model toString!

public enum Player {

NONE, BLUE, WHITE

}

private static void updateViews() {

System.out.println("Updating Views");

}

We will wrap all of this—including our full implementation of Model—into a single class that I am calling ProtoMVC (clever, right?). A copy of this complete stub has been made available to you. [Awww—thanks!]

import java.util.Scanner;

public class ProtoMVC {

public enum Player {

NONE, BLUE, WHITE

}

private static void updateViews() {

System.out.println("Updating Views");

}

public static class Model {

// your Model implementation goes here!

}

public static class Controller {

private Model model;

public Controller (Model model) {

this.model = model;

}

public void evalMove(int row, int col) {

model.setOccupant(row, col, model.getCurrentPlayer());

model.takeTurn();

}

}

public static void main(String[] args) {

Model model = new Model(8);

Controller control = new Controller(model);

Scanner input = new Scanner(System.in);

System.out.println(model);

do {

System.out.print("Enter row col: ");

if (! input.hasNext())

break;

int row = input.nextInt();

int col = input.nextInt();

control.evalMove(row, col);

System.out.print(model);

System.out.println(model.getCount(Player.BLUE));

} while (true);

}

}

Before racing  ahead  and  trying  it  out,  spend  a  few  minutes  studying  the ProtoMVC class.  The  functional components are all designated as static. This is necessary because of the way that ProtoMVC is assembled, with Controller and Model being inner classes, attempting to approximate how the architecture will be realized in the Processing environment. Don't worry about it—just be sure that you have those designations in place; they won't be needed once you move Model to Processing.

Study the main, and how it sequences the construction of the Model and the stub Controller. A Scanner instance is created to make it easy to enter testing commands  [in this case, all we need are  sets of row and column numbers]. Also notice that the state of the Model is being checked by using println to invoke the Model toString method.

Remember: everything you have done to this point is being done using the IntelliJ IDE. You are welcome!

Run the ProtoMVC main and thoroughly test your Model implementation. If it works perfectly, you are ready to proceed to the next  step: the implementation  of View(s). That  step will require  a transition  from IntelliJ to Processing. If your Model is not perfect, fix the problems before moving on! There is no hope of the entire integration working if the Model doesn't do its part!

The output I obtained by running ProtoMVC:

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  . . . . .

. . . B W . . .

. . . W B . . .

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  . . . . .

Enter row col: 1 2

Updating Views

. . B . . . . .

.  .  .  . . . . .

. . . B W . . .

. . . W B . . .

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  . . . . .

3

Enter row col: 1 2

Updating Views

. . W . . . . .

.  .  .  . . . . .

. . . B W . . .

. . . W B . . .

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  . . . . .

2

Enter row col: 0 0

Updating Views

B . . . . . . .

. . W . . . . .

. . . B W . . .

. . . W B . . .

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  . . . . .

3

Enter row col: 7 7

Updating Views

B . . . . . . .

. . W . . . . .

. . . B W . . .

. . . W B . . .

.  .  .  . . . . .

.  .  .  . . . . .

.  .  .  .  .  .  . W

3

The Views

The Controller