首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的生命游戏3

Java中的生命游戏3
EN

Code Review用户
提问于 2013-05-25 13:39:10
回答 2查看 1.6K关注 0票数 2

我有一个版本的John的“Java生活游戏”:

Frame类:

代码语言:javascript
复制
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JToolBar;

public class Frame extends JFrame {
    private Board board;
    private BoardInterface boardInterface;

    private JMenuBar menuBar;
    private JMenu fileMenu, runMenu;
    private JMenuItem newMenuItem, clearMenuItem, randMenuItem, exitMenuItem, runMenuItem, pauseMenuItem;

    private JToolBar toolBar; 
    private JButton run, pause;
    private Image runToolbar, pauseToolbar;

    public Frame(Board board) {
        this.board = board;
        boardInterface = new BoardInterface(board);
        setTitle("The Game of Life");
        setSize(board.getWidth() * board.getMultiplier() + 5, board.getHeight() * board.getMultiplier() + 101);
        setResizable(false);
        setVisible(true);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        add(boardInterface);
        setMenuBar();
        setToolbar();
    }

    public void setMenuBar() {
        menuBar = new JMenuBar();
        add(menuBar, BorderLayout.NORTH);

        fileMenu = new JMenu("File");
        menuBar.add(fileMenu);

        newMenuItem = new JMenuItem("New");
        fileMenu.add(newMenuItem);
        newMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new Frame(new Board());
            }
        });

        fileMenu.add(new JSeparator());

        clearMenuItem = new JMenuItem("Clear Board");
        fileMenu.add(clearMenuItem);
        clearMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                board.setBoard(board.clearBoard());
            }
        });

        randMenuItem = new JMenuItem("Random Board");
        fileMenu.add(randMenuItem);
        randMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                board.setBoard(board.randomBoard());
            }
        });

        fileMenu.add(new JSeparator());

        exitMenuItem = new JMenuItem("Exit");
        fileMenu.add(exitMenuItem);
        exitMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });

        runMenu = new JMenu("Run");
        menuBar.add(runMenu);

        runMenuItem = new JMenuItem("Run");
        runMenu.add(runMenuItem);
        runMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boardInterface.setIsActive(true);
            }
        });

        pauseMenuItem = new JMenuItem("Pause");
        runMenu.add(pauseMenuItem);
        pauseMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boardInterface.setIsActive(false);
            }
        });
    }

    public void setToolbar() {
        toolBar = new JToolBar();
        toolBar.setLayout(new FlowLayout(FlowLayout.CENTER));
        toolBar.setFloatable(false);
        add(toolBar, BorderLayout.SOUTH);

        runToolbar = new ImageIcon("src/playToolbar.png").getImage().getScaledInstance(25, 25, Image.SCALE_SMOOTH);
        run = new JButton(new ImageIcon(runToolbar));
        toolBar.add(run);
        run.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boardInterface.setIsActive(true);
            }
        });

        pauseToolbar = new ImageIcon("src/pauseToolbar.png").getImage().getScaledInstance(25, 25, Image.SCALE_SMOOTH);
        pause = new JButton(new ImageIcon(pauseToolbar));
        toolBar.add(pause);
        pause.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boardInterface.setIsActive(false);
            }
        });
    }


    public static void main(String[] args) {    
        Frame frameInterface = new Frame(new Board());
    }
}

BoardInterface类:

代码语言:javascript
复制
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;
import javax.swing.Timer;

public class BoardInterface extends JPanel implements ActionListener {
    Timer animation = new Timer(500, this);

    private Board board;    
    private boolean isActive;

    public BoardInterface(final Board board) {
        this.board = board;
        setBackground(Color.BLACK);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (!isActive) {
                    board.getBoard()[e.getY() / board.getMultiplier()][e.getX() / board.getMultiplier()] = !board.getBoard()[e.getY() / board.getMultiplier()][e.getX() / board.getMultiplier()];
                }
                else {
                    Toolkit.getDefaultToolkit().beep();
                }
            }
        });
    }

    public void setIsActive(boolean toSet) {
        isActive = toSet;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < board.getHeight(); i++) {
            for (int j = 0; j < board.getWidth(); j++) {
                g.setColor(board.getBoard()[i][j] ? Color.GREEN : Color.GRAY);
                g.fillRect(j * board.getMultiplier(), i * board.getMultiplier(), board.getMultiplier() - 1, board.getMultiplier() - 1);
            }
        }
        if (isActive) {
            animation.restart();
        }
        else {
            animation.stop();
            repaint();
        }
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(animation)) {
            board.nextGeneration();
            repaint();
        }
    }
}

Board类:

代码语言:javascript
复制
import java.util.Random;

public class Board {    
    private boolean[][] board;  
    private int height, width, multiplier = 10;

    public Board() {
        this(new boolean[60][60]);
    }

    public Board(final boolean[][] board) {
        this.board = board;
        height = board.length;
        width = board[0].length;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int n) {
        height = n;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int n) {
        width = n;
    }

    public int getMultiplier() {
        return multiplier;
    }

    public void setMultiplier(int n) {
        multiplier = n;
    }

    public boolean[][] getBoard() {
        return board;
    }

    public void setBoard(boolean[][] n) {
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                board[i][j] = n[i][j];
            }
        }
    }

    public boolean[][] randomBoard() {
        Random rand = new Random();
        boolean[][] randBoard = new boolean[height][width];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                randBoard[i][j] = rand.nextBoolean();
            }
        }
        return randBoard;
    }

    public boolean[][] clearBoard() {
        boolean[][] emptyBoard = new boolean[height][width];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                emptyBoard[i][j] = false;
            }
        }
        return emptyBoard;
    }

    public int max(int i, int j) {
        return i > j ? i : j;
    }

    public int countSurrounding(int a, int b) {
        int count = 0;
        int[][] surrounding = {{a - 1, b - 1},
                               {a - 1, b    },
                               {a - 1, b + 1},
                               {a    , b - 1},
                               {a    , b + 1},
                               {a + 1, b - 1},
                               {a + 1, b    },
                               {a + 1, b + 1}};
        for (int[] i: surrounding) {
            try {
                if (board[i[0]][i[1]]) {
                    count++;
                }
            }
            catch (ArrayIndexOutOfBoundsException e) {}
        }
        return count;
    }

    public void nextGeneration() {
        boolean[][] nextBoard = new boolean[height][width];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                nextBoard[i][j] = board[i][j];
            }
        }
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (board[i][j] && !(countSurrounding(i, j) == 2 || countSurrounding(i, j) == 3)) {
                    nextBoard[i][j] = false;
                }
                else if (!board[i][j] && countSurrounding(i, j) == 3) {
                    nextBoard[i][j] = true;
                }
            }
        }
        board = nextBoard;
    }   
}

我来这里是为了问一个标准的问题:有人能提供一个诚实的代码评审吗?谢谢。

EN

回答 2

Code Review用户

发布于 2013-05-25 14:13:37

我不是Java专家,所以我现在说的不是每句话都是正确的,但以下是我的评论:

首先,您在封装方面做得很好。几乎所有的方法都只是几行代码,所以每个方法实际上只做它应该做的事情。此外,您的代码是一个很好的例子,如果代码编写得很好,就不需要注释来解释所做的事情。因为您应该从代码本身中看到所做的事情。

因此,负面的东西来了:

  • 此外,代码不一定需要说明所做事情的注释,它需要(在我看来)说明为什么要做某事的注释。因此,我忽略了代码中的一些注释,例如,只是简短地解释了类是如何一起工作的,或者,因为我不清楚为什么在构造函数中没有设置板的高度和宽度。
  • 你不应该使用“魔法数字”。这意味着不要硬编码大小或位置或类似的东西。例如,在理事会构造函数中,您可以创建一个大小为60的数组。这很好。但是,如果您想稍后更改大小,那么您也可以查看您编写大小的位置。最好是将值写成类中的常量。因此,您可以很容易地找到并更改它,另外,在类中的任何地方都可以使用它。也许你到了需要这个的地步。同样的建议也适用于您创建JFrame的部分。setSize(board.getWidth() * board.getMultiplier() + 5,board.getHeight() * board.getMultiplier() + 101);您应该将这些数字写成常量。如果您更改您的帧的大小,那么您将不得不改变这个数字以及。因此,您可以更容易地找到它们作为常量。或者更好的,如果这是可能的:动态计算您的帧的大小。
  • 在你的板类中,你有板的高度和宽度的getter和setter。但是为什么设置者的宽度和高度是公开的呢?在每个更新步骤中都使用高度和宽度的值。那么,如果他们在课堂之外的某个地方被改变了,会发生什么呢?当涉及到数组大小时,我的建议是:不要对此值使用setter。当数组已经创建时,任何东西都不能更改边框。只有在数组也被更改时才更改边框。
  • 在方法setBoard中,假设得到的数组与您在对象中持有的数组大小相同(或更大)。您应该快速检查所传递的数组是否具有相同的大小(或者更大,什么是好的,但没有意义)。否则,当传递的数组较小时,程序将崩溃。也不太清楚这种方法应该做什么。传递的数组应该是新数组吗?然后你应该调整你的实习生数组的大小。还是应该像现在这样传递值呢?然后您必须检查数组的大小。
  • 您通常使用非常短的、无法表达的变量或参数名称。这在嵌套的for循环中是可以的(虽然我更喜欢iX和iY而不是i和j,但这只是一种习惯)。但对于参数来说,这是不行的。例如,在"setMultiplier“中使用"n”作为参数,在"setBoard“中使用"n”作为参数。它们的类型和用途不同。您的参数名称应该描述它们的用途。这不需要很长的名字,只需要像"new_Board_Array“或"multiplier_Value”这样的名字。假设您有一个具有多个参数的方法,并且使用了您的命名约定: public int (int,boolundefined m,float k)。那么你的方法就会很混乱。

这就是我注意到的。只是出于好奇:你想给你的生活游戏程序增加更多的功能吗?例如,当我编写一个生命游戏程序时,我添加了一些功能,比如保存curent字段,或者在游戏中返回以查看旧的字段。您也可以实现一些功能,如加快游戏或有自定义的字段大小,而不是固定的60*60。

希望这对你有一点帮助。

票数 1
EN

Code Review用户

发布于 2013-05-25 22:55:06

挺不错的。我同意“摩根斯坦”的评论。

然而,有一个大问题:您经常试图获取countSurrounding()中数组范围之外的元素,并且捕获异常。您不应该使用异常来处理标准条件,因为它们会影响性能。你可以用很多方法解决这个问题。提高效率的一种方法是在创建时预先计算每个单元的邻居。

票数 0
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/26602

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档