CSE190L Model/View/Controller Example handout #12
Driver class Clicker.java
-------------------------
// The Clicker class is the driver for an application written with the Model/
// View/Controller design pattern. It constructs the model and the controller
// and then tells the controller to begin execution. The controller constructs
// the view.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;
public class Clicker {
public static void main(String[] args) {
ClickerModel m = new ClickerModel();
ClickerController c = new ClickerController(m, false);
c.run();
}
}
Interface ClickerListener.java
------------------------------
// Interface ClickerListener is used to establish communication between the
// model and its listeners. All model listeners must implement this interface
// and be registered with the model. The model calls this method on each
// listener every time the model state changes. The model passes a reference
// to itself in case the view needs this (a call-back protocol).
public interface ClickerListener {
public void update(ClickerModel model);
}
Model class ClickerModel.java
-----------------------------
// Class ClickerModel is the model for this application. It keeps track of
// the positions where the two players have clicked. It provides methods for
// getting an iterator over each player's clicked positions. It also provides
// a mechanism for registering listeners whose update method is called each
// time the model changes state.
import java.util.*;
import java.awt.*;
import java.util.List;
public class ClickerModel {
public enum Player {COMPUTER, HUMAN};
private Map> playerPoints; // points for each player
private List listeners; // registered listeners
public ClickerModel() {
playerPoints = new HashMap>();
playerPoints.put(Player.COMPUTER, new HashSet());
playerPoints.put(Player.HUMAN, new HashSet());
listeners = new ArrayList();
}
// Remembers a new point for the given player.
public void add(Point p, Player player) {
playerPoints.get(player).add(p);
updateOccurred();
}
// Returns an iterator for the given player's points
public Iterator getIteratorFor(Player player) {
return playerPoints.get(player).iterator();
}
// Remembers a new listener of the model.
public void addListener(ClickerListener listener) {
listeners.add(listener);
}
// Calls all of the listeners to inform them that an update has occurred.
private void updateOccurred() {
for (ClickerListener listener : listeners)
listener.update(this);
}
}
View class ClickerPanel.java
----------------------------
// Class ClickerPanel is the view for this application. Whenever the model
// tells it to update, it draws all circles with player 1 circles in red and
// player 2 circles in blue.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;
public class ClickerPanel extends JPanel implements ClickerListener {
private ClickerModel model; // the model
public static final int RADIUS = 15; // radius of circles
public ClickerPanel(ClickerModel model) {
this.model = model;
setBackground(Color.GREEN);
}
// The model calls this method when an update has occurred, in which case
// we repaint the view.
public void update(ClickerModel model) {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
drawCircles(g2, Color.RED, ClickerModel.Player.COMPUTER);
drawCircles(g2, Color.BLUE, ClickerModel.Player.HUMAN);
}
// Draws one set of circles in the given color for the given player
private void drawCircles(Graphics2D g2, Color c,
ClickerModel.Player player) {
Iterator i = model.getIteratorFor(player);
Ellipse2D circle = new Ellipse2D.Double();
while (i.hasNext()) {
Point p = i.next();
circle.setFrameFromCenter(p.x, p.y, p.x + RADIUS, p.y + RADIUS);
g2.setPaint(c);
g2.fill(circle);
g2.setPaint(Color.BLACK);
g2.draw(circle);
}
}
}
Controller class ClickerController.java
---------------------------------------
// Class ClickerController is the controller for this application. The idea
// is that the computer and the user alternate turns, clicking where circles
// should appear. This class constructs a single view that has a mouse
// listener. Obviously the computer doesn't actually click anywhere, so this
// class simulates random computer moves when appropriate.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class ClickerController {
private JFrame frame; // top-level application frame
private ClickerModel model; // the model
private ClickerPanel panel; // the view
private boolean userFirst; // is the user supposed to go first?
private Random random; // used to make random computer moves
// Constructs a controller for the given model, with the second argument
// indicating whether or not the user should go first.
public ClickerController(ClickerModel model, boolean userFirst) {
this.model = model;
frame = new JFrame("CSE190L Clicker");
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// construct the view, add it to the frame, register it with the model,
// set up its one control (a mouse listener)
panel = new ClickerPanel(model);
frame.add(panel, BorderLayout.CENTER);
model.addListener(panel);
panel.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
handleClick(e);
}
});
random = new Random();
this.userFirst = userFirst;
}
// launch the program, asking the computer for a move if it is first
public void run() {
frame.setVisible(true);
if (!userFirst)
computerMove();
}
// handle a mouse click from the user
private void handleClick(MouseEvent e) {
model.add(e.getPoint(), ClickerModel.Player.HUMAN);
computerMove();
}
// Makes a random computer move, "clicking" on a random position that will
// lead to a circle that is within the bounds of the view.
private void computerMove() {
int x = ClickerPanel.RADIUS +
random.nextInt(panel.getWidth() - 2 * ClickerPanel.RADIUS);
int y = ClickerPanel.RADIUS +
random.nextInt(panel.getHeight() - 2 * ClickerPanel.RADIUS);
model.add(new Point(x, y), ClickerModel.Player.COMPUTER);
}
}