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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/houjunkang363/article/details/90579508

问题

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

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

image.png

方案

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

        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型----------
    - - - - * * * - - -
    - - - - - - * - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -
    - - - - - - - - - -

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

博主点评:

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

扫码关注我吧:

猜你喜欢

转载自blog.csdn.net/houjunkang363/article/details/90579508