分析案例“俄罗斯方块项目中的T类和J类”中的T类和J类,会发现存在大量的重复代码,比如,cells属性,print方法、drop方法、moveLeft方法、moveRight方法,这四个方法在各个类中的实现都是相同的。因此,本案例要求使用继承的方式,构建T类和J类父类Tetromino类,重构T类和J类并测试重构后的代码。
另外,测试时,需要打印游戏所在的平面(宽10格,高20格),用"-"号表示平面上的每个单元;然后使用"*"号打印显示方块中的每个格子,如图-1中所示。
要实现本案例的功能,解决方案如下:
1.抽取出,T类和J类中共有的属性和方法,然后,构建Tetromino类,将这些共有的属性和方法抽取到Tetromino类中。
2.重构T类为TetrominoT类。在TetrominoT类中,只保留该类特有的部分,比如,该类的构造中,初始化T型方块部分。
3.重构J类为TetrominoJ类。在TetrominoJ类中,也是只保留该类特有的部分,比如,该类的构造中,初始化J型方块部分。
4.测试重构后的代码,构建TetrominoGame类。首先,在TetrominoGame类中,创建方法打印出游戏所在的平面(宽10格,高20格),用"-"号表示平面上的每个单元;然后使用"*"号打印显示方块中的每个格子。
实现此案例需要按照如下步骤进行。
首先,抽取出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++; } } }查看T类和J类带参构造方法中,都对cells数组进行了初始化,因此,对cells数组的初始化也属于两个类的共有部分。所以,定义Tetromino类的无参构造方法,在构造方法中,初始化cells数组的长度为4,代码如下所示:
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++; } } }重构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类为TetrominoJ类,重构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{ 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(); } } }控制台输出结果如下所示:
--------打印T型---------- - - - - * * * - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --------打印J型---------- - - - - * * * - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -从上述代码中打印结果可以看出,父类的引用是可以指向子类的对象的。构造子类对象时,也构造了父类的对象。
当重复的代码比较多时,要考虑将公共的抽出来定义在父类里,子类去继承。
扫码关注我吧: