2010-06-18 23 views
117

Tôi đang thực hành lập trình kiểu MVC. Tôi có một trò chơi Mastermind trong một tập tin duy nhất, làm việc tốt (có thể ngoài thực tế là "Kiểm tra" nút là vô hình lúc bắt đầu).GUI không hoạt động sau khi viết lại cho MVC

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

Nhưng khi tôi đã viết lại nó để mô hình, xem, file điều khiển - và khi tôi bấm vào trống Pin (cần được cập nhật, và sơn lại với màu sắc mới) - ghi nhận xảy ra. Ai có thể nhìn thấy bất kỳ vấn đề ở đây? Tôi đã thử đặt sơn lại() ở những nơi khác nhau, nhưng nó chỉ đơn giản không hoạt động tại tất cả:/

chính:

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); 
    } 
} 

mẫu:

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; 
    } 
} 

Xem:

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); 
    } 
} 

Bộ điều khiển:

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

Cả mã cũ và mới đều có vấn đề liên quan đến việc trộn các thành phần AWT và Swing. Xem thêm http://stackoverflow.com/questions/2687871 – trashgod

+0

để vấn đề không cập nhật có thể do điều đó? –

+0

Có. http://java.sun.com/products/jfc/tsc/articles/mixing/ – trashgod

Trả lời

142

Như bạn đã khám phá, mẫu Model–View–Controller không phải là thuốc chữa bách bệnh, nhưng nó cung cấp một số lợi thế. Bắt nguồn từ MVC, kiến ​​trúc mô hình có thể chia tách Swing được thảo luận trong A Swing Architecture Overview. Dựa trên this outline, ví dụ sau đây cho thấy việc thực hiện MVC của trò chơi đơn giản hơn nhiều, minh họa các nguyên tắc tương tự. Lưu ý rằng Model quản lý một đơn Piece, được chọn ngẫu nhiên. Để phản hồi lựa chọn của người dùng, số View gọi phương thức check() trong khi nghe phản hồi từ Model qua update(). Sau đó, View tự cập nhật thông tin bằng cách sử dụng thông tin thu được từ số Model. Tương tự, Controller có thể reset() số Model. Đặc biệt, không có bản vẽ trong Model và không có logic trò chơi trong View. Điều này phức tạp hơn một chút game được thiết kế để minh họa cho cùng một khái niệm.

Phụ lục: Tôi đã sửa đổi ví dụ ban đầu để hiển thị cách MVC cho phép một tăng cường View mà không thay đổi bản chất của Model.

Phụ lục: Khi @akf quan sát, MVC bản lề trên observer pattern. Model của bạn cần một cách để thông báo cho View các thay đổi. Một số phương pháp được sử dụng rộng rãi:

  • Trong ví dụ dưới đây, Model kéo dài Observable để đơn giản.

  • Cách tiếp cận phổ biến hơn sử dụng EventListenerList, như được hiển thị trong ứng dụng Converter và được đề xuất bởi số lượng lớn EventListener các giao diện con và các lớp triển khai.

  • Tùy chọn thứ ba là sử dụng PropertyChangeListener, như được hiển thị herehere.

Phụ lục: Một số câu hỏi phổ biến về bộ điều khiển xoay được giải quyết .

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: Tôi đã cập nhật ví dụ ở trên. Bạn có thể thấy hữu ích khi so sánh các bản sửa đổi. – trashgod

+0

+1 Tự hỏi làm thế nào MVC có thể được trộn lẫn với n-tier. –

+2

Đối với bất kỳ ai tò mò Fowler đã đưa ra bài viết sau vào năm 2006: http://martinfowler.com/eaaDev/SeparatedPresentation.html –

19

Khi nhìn qua Swing, một trong những cách mà các nhà thiết kế luôn sử dụng cập nhật Xem các thành phần trong việc thực hiện MVC của nó là thông qua Observer/callbacks quan sát được. Một ví dụ có thể được nhìn thấy trong các AbstractTableModel, trong đó có một loạt các phương pháp fireTable*Changed/Updated/etc sẽ cảnh báo tất cả các quan sát của nó TableModelListener quan sát của mods cho mô hình.

Một tùy chọn bạn có là thêm loại trình nghe vào lớp Model của bạn và sau đó thông báo cho các quan sát viên đã đăng ký của bạn về mọi bản sửa đổi về trạng thái của mô hình của bạn. View của bạn phải là người nghe và phải tự sơn lại khi nhận được bản cập nhật.

CHỈNH SỬA: +1 vào thùng rác. xem xét điều này một từ ngữ thay thế để giải thích của mình.

Các vấn đề liên quan