Top

JAVA OOP DAY03

  1. 构建Tetromino类,重构T和J类并测试

1 构建Tetromino类,重构T和J类并测试

1.1 问题

分析案例“定义Tetris项目中的T类和J类并测试”中的T类和J类,会发现存在大量的重复代码,比如,cells属性,print方法、drop方法、moveLeft方法、moveRight方法,这四个方法在各个类中的实现都是相同的。因此,本案例要求使用继承的方式,构建T类和J类父类Tetromino类,重构T类和J类并测试重构后的代码,

另外,测试时, 需要打印出游戏所在的平面(宽10格,高20格),用“-”号表示平面上的每个单元;然后使用“*”号打印显示方块中的每个格子,如图-1中所示。

图- 1

1.2 方案

要实现本案例的功能,解决方案如下:

  1. 测试重构后的代码,构建TetrominoGame类。首先,在TetrominoGame类中,创建方法打印出游戏所在的平面(宽10格,高20格),用“-”号表示平面上的每个单元;然后使用“*”号打印显示方块中的每个格子。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:构建Tetromino类

首先,抽取出T类和J类中共有的属性和方法,然后,构建Tetromino类,将这些共有的属性和方法抽取到Tetromino类中。抽取T类和J类中的cells属性,print方法、drop方法、moveLeft方法以及moveRight方法到Tetromino类中,代码如下所示:

public class Tetromino {
	Cell[] cells;// 属性,用来存储一个方块的四个格子的坐标

	/**
	 * 按顺时针方向,打印方块中四个格子所在的坐标
	 */
	public void print() {
		String str = "";
		for (int i = 0; i < cells.length - 1; i++) {
			str += "(" + cells[i].getCellInfo() + "), ";
		}
		str += "(" + cells[cells.length - 1].getCellInfo() + ")";
		System.out.println(str);

	}

	/**
	 * 使方块下落一个格子
	 */
	public void drop() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].row++;
		}
	}

	/**
	 * 使方块左移一个格子
	 */
	public void moveLeft() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col--;
		}
	}

	/**
	 * 使用方块右移一个格子
	 */
	public void moveRight() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col++;
		}
	}
}

步骤二:定义Tetromino类的构造方法

查看T类和J类带参构造方法中,都对cells数组进行了初始化,因此,对cells数组的初始化也属于两个类的共有部分。所以,定义Tetromino类的无参数构造方法,在构造方法中,初始化cells数组的长度为4,代码如下所示:

public class Tetromino {
	Cell[] cells;// 属性,用来存储一个方块的四个格子的坐标

#cold_bold	/**
#cold_bold	 * 构造方法,初始化cells数组
#cold_bold	 */
#cold_bold	public Tetromino() {
#cold_bold		cells = new Cell[4];
#cold_bold	}
	/**
	 * 按顺时针方向,打印方块中四个格子所在的坐标
	 */
	public void print() {
		String str = "";
		for (int i = 0; i < cells.length - 1; i++) {
			str += "(" + cells[i].getCellInfo() + "), ";
		}
		str += "(" + cells[cells.length - 1].getCellInfo() + ")";
		System.out.println(str);

	}

	/**
	 * 使方块下落一个格子
	 */
	public void drop() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].row++;
		}
	}

	/**
	 * 使方块左移一个格子
	 */
	public void moveLeft() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col--;
		}
	}

	/**
	 * 使用方块右移一个格子
	 */
	public void moveRight() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col++;
		}
	}
}

步骤三:重构T类

重构T类为TetrominoT类,在步骤一和步骤二中,我们已经将T类和J类的共有部分抽取出去。再此,重构T类时,只有构造方法的实现不同。构造方法的实现为根据不同的形状给cells数组的每个元素进行赋值。TetrominoT类的代码如下所示:

public class TetrominoT extends Tetromino {
	public TetrominoT(int row, int col) {
		super();
		// 按顺时针方向初始化Cell
		cells[0] = new Cell(row, col);
		cells[1] = new Cell(row, col + 1);
		cells[2] = new Cell(row, col + 2);
		cells[3] = new Cell(row + 1, col + 1);
	}
}

上述代码中,使用super关键字调用父类的无参数构造方法。代码“super();”是可以省略不写的。默认情况下,系统在子类构造方法的第一句代码就是“super(); ”即,调用父类无参数的构造方法。

步骤四:重构J类

重构J类为TetrominJ类,重构J类时,和重构T是一样的实现,只有构造方法的实现不同。TetrominoJ类的代码如下所示:

public class TetrominoJ extends Tetromino {
	public TetrominoJ(int row, int col) {
		cells[0] = new Cell(row, col);
		cells[1] = new Cell(row, col + 1);
		cells[2] = new Cell(row, col + 2);
		cells[3] = new Cell(row + 1, col + 2);
	}
}

上述代码中,在TetrominoJ类的构造方法的第一句,虽然没有使用super关键字调用父类无参数的构造方法,但是,系统会默认调用父类无参数的构造方法。

步骤五:测试重构后的代码

测试重构后的代码,构建TetrominoGame类。首先,在TetrominoGame类中,创建方法打印出游戏所在的平面(宽10格,高20格),用“-”号表示平面上的每个单元格;然后使用“*”号打印显示方块中的每个格子。TetrominoGame类的代码如下所示:

import java.util.Scanner;

public class TetrominoGame {
	/**
	 * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
	 * 
	 * @param tetromino 需要显示在游戏平面中的方块
	 */
	public static void printTetromino(Tetromino tetromino) {
		int totalRow = 20;
		int totalCol = 10;
		//获取方块中存储的四个格子的数组
		Cell[] cells = tetromino.cells;
		for (int row = 0; row < totalRow; row++) {
			for (int col = 0; col < totalCol; col++) {
				// 用于判断该位置是否包含在cells数组中
				boolean isInCells = false;
				for (int i = 0; i < cells.length; i++) {
					if (cells[i].row == row && cells[i].col == col) {
						System.out.print("* ");
						isInCells = true;
						break;
					}
				}
				if (!isInCells) {
					System.out.print("- ");
				}
			}
			System.out.println();
		}
	}
}

在TetrominoGame类中,添加main方法,在控制台,打印T型和J型。代码如下所示:

public class TetrominoGame {
#cold_bold	public static void main(String[] args) {
#cold_bold		//测试TetrominoT
#cold_bold		System.out.println("--------打印T型---------");
#cold_bold		Tetromino t = new TetrominoT(0, 4);
#cold_bold		printTetromino(t);
#cold_bold		
#cold_bold		//测试TetrominoJ
#cold_bold		System.out.println("--------打印J型---------");
#cold_bold		Tetromino j = new TetrominoJ(0, 4);
#cold_bold		printTetromino(j);
#cold_bold	}

	/**
	 * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
	 * 
	 * @param tetromino 需要显示在游戏平面中的方块
	 */
	public static void printTetromino(Tetromino tetromino) {
		int totalRow = 20;
		int totalCol = 10;
		//获取方块中存储的四个格子的数组
		Cell[] cells = tetromino.cells;
		for (int row = 0; row < totalRow; row++) {
			for (int col = 0; col < totalCol; col++) {
				// 用于判断该位置是否包含在cells数组中
				boolean isInCells = false;
				for (int i = 0; i < cells.length; i++) {
					if (cells[i].row == row && cells[i].col == col) {
						System.out.print("* ");
						isInCells = true;
						break;
					}
				}
				if (!isInCells) {
					System.out.print("- ");
				}
			}
			System.out.println();
		}
	}
}

控制台输出结果如下所示:

--------打印T型---------
- - - - * * * - - - 
- - - - - * - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
--------打印J型---------
- - - - * * * - - - 
- - - - - - * - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - - 
- - - - - - - - - -

从上述代码和打印结果可以看出,父类的引用是可以指向子类的对象的。构造子类对象时,也构造了父类的对象。

1.4 完整代码

本案例中,Tetromino类的完整代码如下所示:

public class Tetromino {
	Cell[] cells;// 属性,用来存储一个方块的四个格子的坐标

	/**
	 * 构造方法,初始化cells数组
	 */
	public Tetromino() {
		cells = new Cell[4];
	}
	/**
	 * 按顺时针方向,打印方块中四个格子所在的坐标
	 */
	public void print() {
		String str = "";
		for (int i = 0; i < cells.length - 1; i++) {
			str += "(" + cells[i].getCellInfo() + "), ";
		}
		str += "(" + cells[cells.length - 1].getCellInfo() + ")";
		System.out.println(str);

	}

	/**
	 * 使方块下落一个格子
	 */
	public void drop() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].row++;
		}
	}

	/**
	 * 使方块左移一个格子
	 */
	public void moveLeft() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col--;
		}
	}

	/**
	 * 使用方块右移一个格子
	 */
	public void moveRight() {
		for (int i = 0; i < cells.length; i++) {
			cells[i].col++;
		}
	}
}

TetrominoT类的完整代码如下所示:

public class TetrominoT extends Tetromino {
	public TetrominoT(int row, int col) {
		super();
		// 按顺时针方向初始化Cell
		cells[0] = new Cell(row, col);
		cells[1] = new Cell(row, col + 1);
		cells[2] = new Cell(row, col + 2);
		cells[3] = new Cell(row + 1, col + 1);
	}
}

TetrominoJ类的完整代码如下所示:

public class TetrominoJ extends Tetromino {
	public TetrominoJ(int row, int col) {
		cells[0] = new Cell(row, col);
		cells[1] = new Cell(row, col + 1);
		cells[2] = new Cell(row, col + 2);
		cells[3] = new Cell(row + 1, col + 2);
	}
}

TetrominoGame类的完整代码如下所示:

public class TetrominoGame {
	public static void main(String[] args) {
		//测试TetrominoT
		System.out.println("--------打印T型---------");
		Tetromino t = new TetrominoT(0, 4);
		printTetromino(t);
		
		//测试TetrominoJ
		System.out.println("--------打印J型---------");
		Tetromino j = new TetrominoJ(0, 4);
		printTetromino(j);
	}

	/**
	 * 打印出游戏所在的平面(宽10格,高20格)。用“-”号表示平面上的每个单元,用“*”号打印显示方块中的每个格子
	 * 
	 * @param tetromino 需要显示在游戏平面中的方块
	 */
	public static void printTetromino(Tetromino tetromino) {
		int totalRow = 20;
		int totalCol = 10;
		//获取方块中存储的四个各自的数组
		Cell[] cells = tetromino.cells;
		for (int row = 0; row < totalRow; row++) {
			for (int col = 0; col < totalCol; col++) {
				// 用于判断该位置是否包含在cells数组中
				boolean isInCells = false;
				for (int i = 0; i < cells.length; i++) {
					if (cells[i].row == row && cells[i].col == col) {
						System.out.print("* ");
						isInCells = true;
						break;
					}
				}
				if (!isInCells) {
					System.out.print("- ");
				}
			}
			System.out.println();
		}
	}
}