Java入门第58课——构建Tetromino类、重构T和J类

    xiaoxiao2025-05-30  96

    问题

            分析案例“俄罗斯方块项目中的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格),用"-"号表示平面上的每个单元;然后使用"*"号打印显示方块中的每个格子。

    步骤

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

    步骤一:构建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;//属性,用来存储一个方块的四个格子的坐标                  /**          *构造方法,初始化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类

            重构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类为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型----------     - - - - * * * - - -     - - - - - - * - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -     - - - - - - - - - -

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

    博主点评:

        当重复的代码比较多时,要考虑将公共的抽出来定义在父类里,子类去继承。

    扫码关注我吧:

     

    最新回复(0)