代码重构-以贪吃蛇为示例(一)-重构之前
题外话:
今天中午做到电脑前没事干,就写个贪吃蛇游戏,写着写着就发现变量太多了,函数调用太乱了,逻辑越来越混乱,于是就想重构一下。但是有想一想,干脆把重构的过程记录下来,大家也分享分享,还能提高一下知名度,何乐而不为呢?
所以……所以……我硬着头皮在不重构的情况下写完了一个简单的贪吃蛇(WTF!)
?
?
/*--------------------------------------------无耻的分隔栏----------------------------------------------------- */
?
介绍下功能:
Swing组件完成的贪吃蛇游戏,通过键盘的方向键改变方向,吃到虫子增长长度,每吃掉一个虫子,移动速度会增长,撞到四周或者自己提示游戏结束。记录分数(但是没显示,因为不重构写不下去了),没有重新游戏(留到重构以后再写)。
?
界面:

?
(恐怖的)代码:
?
package snakes;import java.awt.Color;import java.awt.Graphics;import java.awt.Point;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.Map;import java.util.Random;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;public class Game extends JPanel implements KeyListener {/** * 贪吃蛇行走的方向 */enum Direction {UP {@Overridepublic Point getNextPoint(Point p) {return new Point(p.x, p.y - 1);}@Overridepublic Point getPreviousPoint(Point p) {return new Point(p.x, p.y + 1);}@Overridepublic boolean isAvailable(Direction d) {if (d == Direction.UP || d == Direction.DOWM) {return false;}return true;}},DOWM {@Overridepublic Point getNextPoint(Point p) {return new Point(p.x, p.y + 1);}@Overridepublic Point getPreviousPoint(Point p) {return new Point(p.x, p.y - 1);}@Overridepublic boolean isAvailable(Direction d) {if (d == Direction.UP || d == Direction.DOWM) {return false;}return true;}},LEFT {@Overridepublic Point getNextPoint(Point p) {return new Point(p.x - 1, p.y);}@Overridepublic Point getPreviousPoint(Point p) {return new Point(p.x + 1, p.y);}@Overridepublic boolean isAvailable(Direction d) {if (d == LEFT || d == RIGHT) {return false;}return true;}},RIGHT {@Overridepublic Point getNextPoint(Point p) {return new Point(p.x + 1, p.y);}@Overridepublic Point getPreviousPoint(Point p) {return new Point(p.x - 1, p.y);}@Overridepublic boolean isAvailable(Direction d) {if (d == LEFT || d == RIGHT) {return false;}return true;}};/** * 沿此方向的下一个点 * @param p * @return */public abstract Point getNextPoint(Point p);/** * 沿此方向的上一个点 * @param p * @return */public abstract Point getPreviousPoint(Point p);/** * 可以转动的方向 * @param d * @return */public abstract boolean isAvailable(Direction d);}private static final long serialVersionUID = -7269846451378790762L;private static final Random random = new Random();public static void main(String[] args) {JFrame j = new JFrame("贪吃蛇");Game contentPane = new Game();j.setContentPane(contentPane);j.addKeyListener(contentPane);j.setBounds(20, 20, 700, 500);j.setVisible(true);j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);}/** * 分数 */private int score = 0;/** * 每一个单元格的尺寸,像素 */private final int sellSize = 20;/** * 地图横向包含的单元格数 */private final int tableWidth = 30;/** * 地图纵向包含的单元格数 */private final int tableHeight = 20;/** * 贪吃蛇的点链表 */private final LinkedList<Point> snake = new LinkedList<Point>();private final Direction[] da = { Direction.UP, Direction.DOWM,Direction.LEFT, Direction.RIGHT };private Direction direction = da[random.nextInt(4)];/** * 虫子的位置 */private Point target = new Point(random.nextInt(tableWidth),random.nextInt(tableHeight));/** *贪吃蛇初始长度 */private final int initsnakeLenght = 3;private final Map<Integer, Direction> keyMap = new HashMap<Integer, Direction>();/** * 移动速度 */private volatile long speed = 1;private volatile long crrTime = System.currentTimeMillis();public Game() {keyMap.put(KeyEvent.VK_UP, Direction.UP);keyMap.put(KeyEvent.VK_DOWN, Direction.DOWM);keyMap.put(KeyEvent.VK_LEFT, Direction.LEFT);keyMap.put(KeyEvent.VK_RIGHT, Direction.RIGHT);Point p = new Point(random.nextInt(tableWidth - initsnakeLenght >> 1)+ initsnakeLenght,random.nextInt(tableHeight - initsnakeLenght >> 1)+ initsnakeLenght);snake.add(p);for (int i = 0; i < initsnakeLenght - 1; ++i) {p = direction.getPreviousPoint(p);snake.add(p);}/** * 游戏主循环线程 */new Thread() {@Overridepublic void run() {while (true) {if (System.currentTimeMillis() - crrTime > 500 / speed) {synchronized (Game.class) {moveSnake();if (!checkSnack()) {JOptionPane.showMessageDialog(null,"Game Over!");return;}}repaint();crrTime = System.currentTimeMillis();}}};}.start();}/** * 判断贪吃蛇是否撞墙或撞到自己 * @return */protected boolean checkSnack() {Point p = snake.getFirst();int x = p.x, y = p.y;if (x < 0 || x >= tableWidth || y < 0 || y >= tableHeight) {return false;}Iterator<Point> it = snake.iterator();it.next();while (it.hasNext()) {Point pBody = it.next();if (p.equals(pBody)) {return false;}}return true;}@Overridepublic void keyPressed(KeyEvent e) {}@Overridepublic void keyReleased(KeyEvent e) {Direction newd = keyMap.get(e.getKeyCode());if (newd != null && direction.isAvailable(newd)) {direction = newd;synchronized (Game.class) {moveSnake();if (!checkSnack()) {JOptionPane.showMessageDialog(null, "Game Over!");return;}}repaint();crrTime = System.currentTimeMillis();}}@Overridepublic void keyTyped(KeyEvent e) {}/** * 移动贪吃蛇,包括吃虫 */private void moveSnake() {snake.addFirst(direction.getNextPoint(snake.getFirst()));if (snake.getFirst().equals(target)) {target = new Point(random.nextInt(tableWidth),random.nextInt(tableHeight));++speed;++score;} else {snake.removeLast();}}/** * 绘制图形 */@Overrideprotected void paintComponent(Graphics g) {g.setColor(new Color(0x555555));g.clearRect(0, 0, tableWidth * sellSize, tableHeight * sellSize);for (int i = 0; i < tableWidth; i++) {for (int j = 0; j < tableHeight; ++j) {g.drawRect(i * sellSize, j * sellSize, sellSize, sellSize);}}g.setColor(new Color(0x3399cc));for (Point p : snake) {g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize);}g.setColor(new Color(0x115599));Point p = snake.peek();g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize);g.setColor(new Color(0xdd7744));g.fillRect(target.x * sellSize, target.y * sellSize, sellSize, sellSize);}}?下节预告:拆分文件