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<Player, Set<Point>> playerPoints; // points for each player private List<ClickerListener> listeners; // registered listeners public ClickerModel() { playerPoints = new HashMap<Player, Set<Point>>(); playerPoints.put(Player.COMPUTER, new HashSet<Point>()); playerPoints.put(Player.HUMAN, new HashSet<Point>()); listeners = new ArrayList<ClickerListener>(); } // 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<Point> 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<Point> 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); } }
Stuart Reges
Last modified: Fri Apr 27 10:22:51 PDT 2007