欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从代码的修改来看代码的编写之道 博客分类: Java学习 javaio 

程序员文章站 2024-03-16 19:37:40
...

        最近在学习Java中的IO流时,拿着以前写的五子棋来练手,就是实现五子棋的游戏存档和存档读取。经过几次修改代码后,忽然对代码的编写之道有了一些自己的看法。

        首先介绍一下我的界面:


从代码的修改来看代码的编写之道
            
    
    博客分类: Java学习 javaio 
 

这是一张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):这是界面的大小)
从代码的修改来看代码的编写之道
            
    
    博客分类: Java学习 javaio 
 
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数据也是用数组来存储数据的)。
从这几次修改代码我们可以明白,作为一个程序员,我们写的代码不能只成为一次性的消费品,我们应该写能够在其他程序中也能使用的代码,还应该对我们写的代码的时间复杂度和空间复杂度负责,并且我们写的代码应该要做好我们接到的任务中的所有事情,避免对其他类的代码修改需要。
这就是我从我这几次修改我的代码中获得的总结,谢谢大家的阅读!也欢迎大家提出自己的意见!我们一起交流进步,成为最强大的程序员!
 
我把我的代码打包贴了上来,大家可以下载去看。
  • 从代码的修改来看代码的编写之道
            
    
    博客分类: Java学习 javaio 
  • 大小: 77.8 KB
  • 从代码的修改来看代码的编写之道
            
    
    博客分类: Java学习 javaio 
  • 大小: 20.3 KB
相关标签: java io