2010-06-18 14 views
117

Estoy practicando la programación de estilo MVC. Tengo un juego de Mastermind en un solo archivo, que funciona bien (quizás por el hecho de que el botón "Comprobar" es invisible al inicio).GUI no funciona después de volver a escribir a MVC

http://paste.pocoo.org/show/226726/

Pero cuando he reescrito para modelar, ver archivos de los controladores - y cuando hago clic en vacío Pin (que debe ser actualizado, y pintado con color nuevo) - señalando sucede. ¿Alguien puede ver algún problema aquí? He intentado colocar volver a pintar() en diferentes lugares, pero simplemente no funciona en absoluto:/

principal:

public class Main { 
    public static void main(String[] args){ 
     Model model = new Model(); 
     View view = new View("Mastermind", 400, 590, model); 
     Controller controller = new Controller(model, view); 
     view.setVisible(true); 
    } 
} 

Modelo:

import java.util.Random; 

public class Model{ 
    static final int 
    LINE = 5, 
    SCORE = 10, OPTIONS = 20; 
    Pin pins[][] = new Pin[21][LINE]; 
    int combination[] = new int[LINE]; 
    int curPin = 0; 
    int turn = 1; 
    Random generator = new Random(); 
    int repaintPin; 
    boolean pinsRepaint=false; 
    int pinsToRepaint; 
    boolean isUpdate = true, isPlaying = true, isRowFull = false; 
    static final int HIT_X[] = {270,290,310,290,310}, HIT_Y[] = {506,496,496,516,516}; 

    public Model(){ 

     for (int i=0; i < SCORE; i++){ 
      for (int j = 0; j < LINE; j++){ 
       pins[i][j] = new Pin(20,0); 
       pins[i][j].setPosition(j*50+30,510-i*50); 
       pins[i+SCORE][j] = new Pin(8,0); 
       pins[i+SCORE][j].setPosition(HIT_X[j],HIT_Y[j]-i*50); 
      } 
     } 
     for (int i=0; i < LINE; i++){ 
      pins[OPTIONS][i] = new Pin(20, i+2); 
      pins[OPTIONS][i].setPosition(370,i * 50 + 56); 
     } 

    } 

    void fillHole(int color) { 
     pins[turn-1][curPin].setColor(color+1); 
     pinsRepaint = true; 
     pinsToRepaint = turn; 
     curPin = (curPin+1) % LINE; 
     if (curPin == 0){ 
      isRowFull = true; 
     } 
     pinsRepaint = false; 
     pinsToRepaint = 0; 
    } 

    void check() { 
     int junkPins[] = new int[LINE], junkCode[] = new int[LINE]; 
     int pinCount = 0, pico = 0; 

     for (int i = 0; i < LINE; i++) { 
      junkPins[i] = pins[turn-1][i].getColor(); 
      junkCode[i] = combination[i]; 
     } 
     for (int i = 0; i < LINE; i++){ 
      if (junkPins[i]==junkCode[i]) { 
       pins[turn+SCORE][pinCount].setColor(1); 
       pinCount++; 
       pico++; 
       junkPins[i] = 98; 
       junkCode[i] = 99; 
      } 
     } 
     for (int i = 0; i < LINE; i++){ 
      for (int j = 0; j < LINE; j++) 
       if (junkPins[i]==junkCode[j]) { 
        pins[turn+SCORE][pinCount].setColor(2); 
        pinCount++; 
        junkPins[i] = 98; 
        junkCode[j] = 99; 
        j = LINE; 
      } 
     } 
     pinsRepaint = true; 
     pinsToRepaint = turn + SCORE; 
     pinsRepaint = false; 
     pinsToRepaint=0; 

     if (pico == LINE){ 
      isPlaying = false; 
     } 
     else if (turn >= 10){ 
       isPlaying = false; 
     } 
     else{ 
      curPin = 0; 
      isRowFull = false; 
      turn++; 
     } 
    } 

    void combination() { 
     for (int i = 0; i < LINE; i++){ 
      combination[i] = generator.nextInt(6) + 1; 
     } 
    } 
} 

class Pin{ 
    private int color, X, Y, radius; 

    public Pin(){ 
     X = 0; Y = 0; radius = 0; color = 0; 
    } 

    public Pin(int r,int c){ 
     X = 0; Y = 0; radius = r; color = c; 
    } 

    public int getX(){ 
     return X; 
    } 

    public int getY(){ 
     return Y; 
    } 

    public int getRadius(){ 
     return radius; 
    } 

    public void setRadius(int r){ 
     radius = r; 
    } 

    public void setPosition(int x,int y){ 
     this.X = x ; 
     this.Y = y ; 
    } 
    public void setColor(int c){ 
     color = c; 
    } 
    public int getColor() { 
     return color; 
    } 
} 

Vista:

import java.awt.*; 
import javax.swing.*; 

public class View extends Frame{ 
    Model model; 
    JButton checkAnswer; 
    private JPanel button; 
    private static final Color COLORS[] = {Color.black, Color.white, Color.red, Color.yellow, Color.green, Color.blue, new Color(7, 254, 250)}; 

    public View(String name, int w, int h, Model m){ 
     model = m; 
     setTitle(name); 
     setSize(w,h); 
     setResizable(false); 
     this.setLayout(new BorderLayout()); 

     button = new JPanel(); 
     button.setSize(new Dimension(400, 100)); 
     button.setVisible(true); 
     checkAnswer = new JButton("Check"); 
     checkAnswer.setSize(new Dimension(200, 30)); 
     button.add(checkAnswer); 
     this.add(button, BorderLayout.SOUTH); 
     button.setVisible(true); 
    } 

    @Override 
    public void paint(Graphics g) { 
     g.setColor(new Color(238, 238, 238)); 
     g.fillRect(0,0,400,590); 

     for (int i=0; i < model.pins.length; i++) { 
      paintPins(model.pins[i][0],g); 
      paintPins(model.pins[i][1],g); 
      paintPins(model.pins[i][2],g); 
      paintPins(model.pins[i][3],g); 
      paintPins(model.pins[i][4],g); 
     } 
    } 

    @Override 
    public void update(Graphics g) { 
     if (model.isUpdate) { 
      paint(g); 
     } 
     else { 
      model.isUpdate = true; 
      paintPins(model.pins[model.repaintPin-1][0],g); 
      paintPins(model.pins[model.repaintPin-1][1],g); 
      paintPins(model.pins[model.repaintPin-1][2],g); 
      paintPins(model.pins[model.repaintPin-1][3],g); 
      paintPins(model.pins[model.repaintPin-1][4],g); 
     } 
    } 

    void repaintPins(int pin) { 
     model.repaintPin = pin; 
     model.isUpdate = false; 
     repaint(); 
    } 

    public void paintPins(Pin p, Graphics g){ 
     int X = p.getX(); 
     int Y = p.getY(); 
     int color = p.getColor(); 
     int radius = p.getRadius(); 
     int x = X-radius; 
     int y = Y-radius; 

     if (color > 0){ 
      g.setColor(COLORS[color]); 
      g.fillOval(x,y,2*radius,2*radius); 
     } 
     else{ 
      g.setColor(new Color(238, 238, 238)); 
      g.drawOval(x,y,2*radius-1,2*radius-1); 
     } 
     g.setColor(Color.black); 
     g.drawOval(x,y,2*radius,2*radius); 
    } 
} 

Controlador:

import java.awt.*; 
import java.awt.event.*; 

public class Controller implements MouseListener, ActionListener { 
    private Model model; 
    private View view; 

    public Controller(Model m, View v){ 
     model = m; 
     view = v; 

     view.addWindowListener(new WindowAdapter(){ 
      public void windowClosing(WindowEvent e){ 
      System.exit(0); 
     } }); 
     view.addMouseListener(this); 
     view.checkAnswer.addActionListener(this); 
     model.combination(); 
    } 

    public void actionPerformed(ActionEvent e) { 
     if(e.getSource() == view.checkAnswer){ 
      if(model.isRowFull){ 
       model.check(); 
      } 
     } 
    } 

    public void mousePressed(MouseEvent e) { 
     Point mouse = new Point(); 

     mouse = e.getPoint(); 
     if (model.isPlaying){ 
      if (mouse.x > 350) { 
       int button = 1 + (int)((mouse.y - 32)/50); 
       if ((button >= 1) && (button <= 5)){ 
        model.fillHole(button); 
        if(model.pinsRepaint){ 
         view.repaintPins(model.pinsToRepaint); 
        } 
       } 
      } 
     } 
    } 

    public void mouseClicked(MouseEvent e) {} 
    public void mouseReleased(MouseEvent e){} 
    public void mouseEntered(MouseEvent e) {} 
    public void mouseExited(MouseEvent e) {} 
} 
+5

Tanto el código antiguo como el nuevo tienen problemas relacionados con la mezcla de componentes AWT y Swing. Consulte también http://stackoverflow.com/questions/2687871 – trashgod

+0

para que el problema con la falta de actualización pueda ser causado por eso? –

+0

Sí. http://java.sun.com/products/jfc/tsc/articles/mixing/ – trashgod

Respuesta

142

Como has descubierto, el patrón Model–View–Controller no es una panacea, pero ofrece algunas ventajas. Enraizado en MVC, la arquitectura del modelo separable Swing se trata en A Swing Architecture Overview. Basado en this outline, el siguiente ejemplo muestra una implementación de MVC de un juego mucho más simple que ilustra principios similares. Tenga en cuenta que el Model administra un solo Piece, elegido al azar. En respuesta a la selección de un usuario, el View invoca el método check(), mientras escuchas una respuesta del Model a través del update(). El View luego se actualiza usando la información obtenida del Model. Del mismo modo, el Controller puede reset() el Model. En particular, no hay dibujo en el Model y no hay lógica de juego en el View. Este algo más complejo game fue diseñado para ilustrar los mismos conceptos.

Adición: He modificado el ejemplo original para mostrar cómo MVC permite mejorar el View sin cambiar la naturaleza del Model.

Adición: Como @akf observa, MVC depende del observer pattern. Su Model necesita una forma de notificar al View de los cambios. Varios enfoques son ampliamente utilizados:

Adición: Algunas preguntas comunes acerca de los controladores de oscilación se abordan here y here.

screen capture

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Observable; 
import java.util.Observer; 
import java.util.Random; 
import javax.swing.Icon; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 

/** 
* @see https://stackoverflow.com/q/3066590/230513 
* 15-Mar-2011 r8 https://stackoverflow.com/questions/5274962 
* 26-Mar-2013 r17 per comment 
*/ 
public class MVCGame implements Runnable { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new MVCGame()); 
    } 

    @Override 
    public void run() { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(new MainPanel()); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 
} 

class MainPanel extends JPanel { 

    public MainPanel() { 
     super(new BorderLayout()); 
     Model model = new Model(); 
     View view = new View(model); 
     Control control = new Control(model, view); 
     JLabel label = new JLabel("Guess what color!", JLabel.CENTER); 
     this.add(label, BorderLayout.NORTH); 
     this.add(view, BorderLayout.CENTER); 
     this.add(control, BorderLayout.SOUTH); 
    } 
} 

/** 
* Control panel 
*/ 
class Control extends JPanel { 

    private Model model; 
    private View view; 
    private JButton reset = new JButton("Reset"); 

    public Control(Model model, View view) { 
     this.model = model; 
     this.view = view; 
     this.add(reset); 
     reset.addActionListener(new ButtonHandler()); 
    } 

    private class ButtonHandler implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      String cmd = e.getActionCommand(); 
      if ("Reset".equals(cmd)) { 
       model.reset(); 
      } 
     } 
    } 
} 

/** 
* View 
*/ 
class View extends JPanel { 

    private static final String s = "Click a button."; 
    private Model model; 
    private ColorIcon icon = new ColorIcon(80, Color.gray); 
    private JLabel label = new JLabel(s, icon, JLabel.CENTER); 

    public View(Model model) { 
     super(new BorderLayout()); 
     this.model = model; 
     label.setVerticalTextPosition(JLabel.BOTTOM); 
     label.setHorizontalTextPosition(JLabel.CENTER); 
     this.add(label, BorderLayout.CENTER); 
     this.add(genButtonPanel(), BorderLayout.SOUTH); 
     model.addObserver(new ModelObserver()); 
    } 

    private JPanel genButtonPanel() { 
     JPanel panel = new JPanel(); 
     for (Piece p : Piece.values()) { 
      PieceButton pb = new PieceButton(p); 
      pb.addActionListener(new ButtonHandler()); 
      panel.add(pb); 
     } 
     return panel; 
    } 

    private class ModelObserver implements Observer { 

     @Override 
     public void update(Observable o, Object arg) { 
      if (arg == null) { 
       label.setText(s); 
       icon.color = Color.gray; 
      } else { 
       if ((Boolean) arg) { 
        label.setText("Win!"); 
       } else { 
        label.setText("Keep trying."); 
       } 
      } 
     } 
    } 

    private class ButtonHandler implements ActionListener { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      PieceButton pb = (PieceButton) e.getSource(); 
      icon.color = pb.piece.color; 
      label.repaint(); 
      model.check(pb.piece); 
     } 
    } 

    private static class PieceButton extends JButton { 

     Piece piece; 

     public PieceButton(Piece piece) { 
      this.piece = piece; 
      this.setIcon(new ColorIcon(16, piece.color)); 
     } 
    } 

    private static class ColorIcon implements Icon { 

     private int size; 
     private Color color; 

     public ColorIcon(int size, Color color) { 
      this.size = size; 
      this.color = color; 
     } 

     @Override 
     public void paintIcon(Component c, Graphics g, int x, int y) { 
      Graphics2D g2d = (Graphics2D) g; 
      g2d.setRenderingHint(
       RenderingHints.KEY_ANTIALIASING, 
       RenderingHints.VALUE_ANTIALIAS_ON); 
      g2d.setColor(color); 
      g2d.fillOval(x, y, size, size); 
     } 

     @Override 
     public int getIconWidth() { 
      return size; 
     } 

     @Override 
     public int getIconHeight() { 
      return size; 
     } 
    } 
} 

/** 
* Model 
*/ 
class Model extends Observable { 

    private static final Random rnd = new Random(); 
    private static final Piece[] pieces = Piece.values(); 
    private Piece hidden = init(); 

    private Piece init() { 
     return pieces[rnd.nextInt(pieces.length)]; 
    } 

    public void reset() { 
     hidden = init(); 
     setChanged(); 
     notifyObservers(); 
    } 

    public void check(Piece guess) { 
     setChanged(); 
     notifyObservers(guess.equals(hidden)); 
    } 
} 

enum Piece { 

    Red(Color.red), Green(Color.green), Blue(Color.blue); 
    public Color color; 

    private Piece(Color color) { 
     this.color = color; 
    } 
} 
+1

@trevor_nise: He actualizado el ejemplo anterior. Puede ser útil comparar las revisiones. – trashgod

+0

+1 Se pregunta cómo se puede mezclar MVC con n-tier. –

+2

Para cualquiera que sea curioso, Fowler colocó el siguiente artículo en 2006: http://martinfowler.com/eaaDev/SeparatedPresentation.html –

19

Al mirar a través Swing, una manera de que los diseñadores emplean constantemente la actualización de componentes de la vista en su aplicación MVC es a través del observador/devoluciones de llamada observable. Se puede ver un ejemplo en el AbstractTableModel, que tiene una variedad de métodos fireTable*Changed/Updated/etc que alertarán a todos sus TableModelListener observadores de modificaciones al modelo.

Una opción que tiene es agregar un tipo de oyente a su clase Model, y luego notificar a sus observadores registrados de cualquier modificación al estado de su modelo. Su View debe ser un oyente, y debe volver a pintarse al recibir una actualización.

EDITAR: +1 a trashgod. considere esto una fraseología alternativa a su explicación.

Cuestiones relacionadas