从代码的修改来看代码的编写之道 博客分类: Java学习 javaio
程序员文章站
2024-03-16 19:37:40
...
最近在学习Java中的IO流时,拿着以前写的五子棋来练手,就是实现五子棋的游戏存档和存档读取。经过几次修改代码后,忽然对代码的编写之道有了一些自己的看法。
首先介绍一下我的界面:
这是一张15*15的五子棋标准棋盘,我找了一张棋盘的照片,然后用ps测出他的颜色为:
new Color(241,193,121)还有一些基础数据为:
private int length_i = 15;//此为行数 private int length_j = 15;//此为列数 private int edge_length = 30;//此为边界长度 private int cell_length = 40;//此为单元格长度 private int[][] chessArray = new int[length_i][length_j];//存储棋子的数组对了,我新建了一个JPanel对象来当棋子的画板,并把它加到界面上作为中间面板,这样就可以避免界面上这个框对我们实际得到的x坐标,y坐标的影响。(new Dimension(780, 680):这是界面的大小)
GoListener gl=new GoListener(chessArray,this,edge_length,cell_length); this.addMouseListener(gl);为我们的画板(即中间面板)加上鼠标监听器。
public class GoListener implements MouseListener,ActionListener{ private int x,y,i,j,count=1;//count为棋子次序 private int edge_length,cell_length; private Graphics g; private int[][]array;//0是空,大于0的,奇数为黑子,偶数为白子 private GoBang frame; private boolean flag=false;//是否有胜利者的标识符 /** * * @param array * @param frame * @param edge_length 边界长度 * @param cell_length 单元格长度 */ public GoListener(int[][] array,GoBang frame, int edge_length, int cell_length){ this.array=array; this.frame=frame; this.edge_length=edge_length; this.cell_length=cell_length; } public void setGraphics(Graphics g){ this.g=g; }妥妥的,我们的监听器类,大家可以看到它还继承操作监听器,所以我把传递画笔的代码写到了构造方法之外。有人可能会问,为什么不让使用这几行代码来获得画笔g?
public GoListener(int[][] array,GoBang frame, int edge_length, int cell_length){ this.array=array; this.frame=frame; this.edge_length=edge_length; this.cell_length=cell_length; this.g=frame.getGraphics(); }哈哈,眼尖的读者肯定看到了,我们传递的是整个界面:GoBang frame;如果写成上面那样,我们就还要传递个中间面板过来,然后再在其上获取画笔,这岂不是多此一举?
public void mouseClicked(MouseEvent e) { // 将g强制转换为Graphics2D类型的对象 Graphics2D g2d = (Graphics2D) g; if(flag==false){ //将棋子下在交叉点 x = e.getX();y = e.getY();//获得鼠标点击点的x坐标和y坐标 for (j = 0; j < 15; j++) {//行 b = 30 + 40 * j; if (Math.abs(x - b) < 25) break; } for (i = 0; i < 15; i++) {//列 a = 30 + 40 * i; if (Math.abs(y - a) < 25) break; } x = b;y = a; if(i>=0&&i<=14&&j>=0&&j<=14){//棋子需下在棋盘内 if(array[i][j]==0){//只有下在空的交叉点上 if(count%2==1){ //black棋 g2d.setColor(new Color(65,52,43)); array[i][j]=count;//记录棋子的次序 count++;//次序加1 }else{ //white棋 g2d.setColor(new Color(248,248,238)); array[i][j]=count; count++; } g2d.fillOval(x - 20, y - 20, 40, 40); //Judge为判定输赢得类,需要传入存储数据的数组 Judge judge=new Judge(array); flag=judge.isWin(i,j); //Judge类中的isWin()方法,需要传入所下棋子的行数和列数,如果有胜利者则返回true;没有则返回false; if(flag){ if(array[i][j]%2==1)//次序为奇数,下的是黑棋 JOptionPane.showMessageDialog(null, "黑棋赢了!"); if(array[i][j]%2==0)//次序为非零偶数,下的是白棋 JOptionPane.showMessageDialog(null, "白棋赢了!"); }//判定输赢 }//如果交叉点上有棋子,就什么事都不做。 }//如果鼠标点击点不在棋盘内,就什么事也不做。 }else{ int str=JOptionPane.showConfirmDialog(null,"是否开始新游戏" ); if(str==0) Restart(); } }
这里我用了两个for循环来获得棋子所想要的行数 i 和列数 j ,然后将count存在array[i][j]中,count即正在下的棋子的次序,画完棋子后,count++。
好的,于是我们的第一个变革点来了
public void mouseClicked(MouseEvent e) { // 将g强制转换为Graphics2D类型的对象 Graphics2D g2d = (Graphics2D) g; if(flag==false){ x = e.getX();y = e.getY();//获得鼠标点击点的x坐标和y坐标 //除法公式 i = (y-edge_length+cell_length/2)/cell_length; j = (x-edge_length+cell_length/2)/cell_length; //将棋子下在交叉点 x = edge_length+j*cell_length;y = edge_length+i*cell_length; if(i>=0&&i<=14&&j>=0&&j<=14){ if(array[i][j]==0){ System.out.println(i+">>>"+j); if(count%2==1){ //black g2d.setColor(new Color(65,52,43)); array[i][j]=count; count++; }else{ //white g2d.setColor(new Color(248,248,238)); array[i][j]=count; count++; } g2d.fillOval(x - cell_length/2, y - cell_length/2, cell_length, cell_length); //判断棋子输赢 Judge judge=new Judge(array); flag=judge.isWin(i,j); if(flag){ if(array[i][j]%2==1) JOptionPane.showMessageDialog(null, "黑棋赢了!"); if(array[i][j]%2==0) JOptionPane.showMessageDialog(null, "白棋赢了!"); }//判定输赢 }//如果交叉点上有棋子,就什么事都不做。 }//如果鼠标点击点不在棋盘内,就什么事也不做。 }else{ int str=JOptionPane.showConfirmDialog(null,"是否开始新游戏" ); if(str==0) Restart(); } }是的,我们改变了判定行数和列数的代码,之前的代码的时间复杂度为O(n),现在的时间复杂度为O(1)。
小小的一个改变,收益巨大,也许你现在感觉不到什么,但是以后你写大型程序的时候,你就会知道代码所需要的高效性有多重要了。
我们接着往下,接着就是我们的游戏存档了:
new ArrayToFile(array);//传入存储数据的数组简简单单的一行代码,干完我们所需要的所有事情,这是大家在团队合作中的最好的工作结果,别人只要创建对象,就能做完交给你的所有事情(我交给ArrayTOFile类的事情就是让它把数组存成文件)。
继续当然就是存档读取:
public class FileToArray { private int[][] array=new int[15][15]; private InputStream ins; private DataInputStream read_file; private int i,j; //此行代码是为了能够一直读下去,而不是从头开始。 public FileToArray(){ try{ /**检测D盘是否存在GoBang这个文件。*/ File filefound=new File("D://Go.tx"); if(filefound.exists()){ ins=new FileInputStream(filefound); read_file=new DataInputStream(ins); FileRead(); System.out.println(">>>"); } else{ JOptionPane.showMessageDialog(null, "没有存档!请存档。"); } }catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } /** * 构造函数 * @throws IOException */ public void FileRead() throws IOException{ while((i=read_file.readInt())!=99 ){ j=read_file.readInt(); array[i][j]=read_file.readInt(); System.out.println(i+" "+j+" "+array[i][j]); } System.out.println("输出完毕!\n\n\n"); } public int[][] getArray() { return array; } }可以看到,我在这个类中new了一个15*15的数组对象来存储从文件中读取的数据。然后用一个getArray()方法来返回这个数组。
调用出的代码如下:
array=new FileToArray().getArray(); count=getMax(array)+1; //判断棋子输赢 Judge judge=new Judge(array); judge.isWin(getMax_i(array),getMax_j(array)); frame.setChessArray(array); //将数组设置回去。其余的不用设置是因为他们在内存中的地址和从GoBang传来的数组一样,而这里改变了数组array的地址。这是我的paint(Graphics g)方法:
public void paint(Graphics g){ super.paint(g); // 将g强制转换为Graphics2D类型的对象 Graphics2D g2d = (Graphics2D) g; g2d.setBackground(new Color(190,145,103)); // 棋盘的起始坐标是30,30;横线和竖线各15条;格子的宽度高度是40。 for (int i = 0; i < 15; i++) { if (i == 0 || i == 14) g2d.setStroke(new BasicStroke(5));// 设置线条的粗细 else g2d.setStroke(new BasicStroke(2));// 设置线条的粗细 // 绘制横线和竖线 g2d.drawLine(edge_length, edge_length + cell_length * i, edge_length + 14 * cell_length, edge_length + cell_length * i); g2d.drawLine(edge_length + cell_length * i, edge_length, edge_length + cell_length * i, edge_length + 14 * cell_length); } // 绘制矩形 g2d.setStroke(new java.awt.BasicStroke(4)); g2d.drawRect(edge_length, edge_length, cell_length*14, cell_length*14); g2d.fillRect(145, 145, 10, 10); g2d.fillRect(465, 145, 10, 10); g2d.fillRect(465, 465, 10, 10); g2d.fillRect(145, 465, 10, 10); g2d.fillRect(305, 305, 10, 10); // 把下过的棋子重绘一次 for(int i=0;i<chessArray.length;i++){ for(int j=0;j<chessArray[i].length;j++){ if(chessArray[i][j]!=0){ if(chessArray[i][j]%2==1){ g.setColor(new Color(65,52,43)); }else{ g.setColor(new Color(248,248,238)); } g2d.fillOval(10+cell_length*j, 10+cell_length*i, cell_length, cell_length); } } } }
大家按照上面的判定标准肯定可以看出我这个FileToArray类写的很水,为什么呢?因为别人(GoListener对象调用我还需要让GoBang类增加一个setArray(int[][] array)方法(如果没有此函数,那么重绘时就没有我们从文件中读取的数组的事情了),如此兴师动众。
于是我将代码改成了这样:
public class FileToArray { private Reader read_file; private int i,j; //此行代码是为了能够一直读下去,而不是从头开始。 public FileToArray(int[][] array){ Array_clear(array); try{ /**检测D盘是否存在GoBang这个文件。*/ File filefound=new File("D://Go.tx"); if(filefound.exists()){ read_file=new FileReader(filefound); FileRead(array); } else{ JOptionPane.showMessageDialog(null, "没有存档!请存档。"); } }catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } /** * 构造函数 * @param array * @throws IOException */ public void FileRead(int[][] array) throws IOException{ while((i=read_file.read())!=99 ){ j=read_file.read(); array[i][j]=read_file.read(); System.out.println(i+" "+j+" "+array[i][j]); } System.out.println("输出完毕!\n\n\n"); } /** * 清空一个数组 */ public void Array_clear(int[][] array){ for(int i=0;i<array.length;i++){ for (int j=0;j<array[i].length;j++){ array[i][j]=0; } } } }我传入了存储数据的数组,哈哈,如此,我们在这边对数组做的改变,GoBang类里就可以直接感受到了(因为是传址的)。
那样调用处的代码就改成了这样:
new FileToArray(array);//这里用的是传址发 count=getMax(array)+1; //判断棋子输赢 Judge judge=new Judge(array); judge.isWin(getMax_i(array),getMax_j(array)); frame.repaint();good!现在我们完成任务同样只需要创建一个对象了。
而且现在的FileToArray类不仅仅只能读取15*15的棋子数组了,它能读取任何行数和任何列数的二维数组。
我将我的ArrayToFile类和FileToArray类复制到我的2048游戏里,同样只通过创建一个对象,就完成了我的2048的游戏存档和存档读取功能(因为我的2048数据也是用数组来存储数据的)。
从这几次修改代码我们可以明白,作为一个程序员,我们写的代码不能只成为一次性的消费品,我们应该写能够在其他程序中也能使用的代码,还应该对我们写的代码的时间复杂度和空间复杂度负责,并且我们写的代码应该要做好我们接到的任务中的所有事情,避免对其他类的代码修改需要。
这就是我从我这几次修改我的代码中获得的总结,谢谢大家的阅读!也欢迎大家提出自己的意见!我们一起交流进步,成为最强大的程序员!
我把我的代码打包贴了上来,大家可以下载去看。