`

从代码的修改来看代码的编写之道

阅读更多

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

相关推荐

    vb编写记事本,附带源代码,可跟着学习制作

    这种实践方式对于编程初学者来说是非常有价值的,因为直接看成品代码比理论学习更能让人们理解编程逻辑。 “希望对你能有帮助”表明这个资源是一个教学性质的共享,旨在帮助那些想要学习VB编程或增强其GUI应用开发...

    vb编写的小工具,源代码

    从压缩包子文件的文件名来看: 1. "jldx.bat" - 这可能是一个批处理文件,通常用于自动化执行一系列DOS命令。在VB项目中,它可能是用于编译或运行程序的辅助脚本。 2. "Form2.frm" 和 "Form5.frm" - 这些是VB中的...

    Verilog代码编写规范

    首先,我们来看一下Verilog代码编写规范的主要目标: 1. **缩小篇幅提高整洁度**:简洁的代码更容易跟踪和分析,从而降低调试和修改的难度。 2. **增强可读性**:清晰的命名和结构化的代码帮助阅读者快速理解功能和...

    一款方便编写js代码的工具

    总的来说,这样的JS代码编写工具是提升开发效率、减少错误的关键。它通过智能提示、代码高亮、错误检查等功能,为JavaScript程序员创造了一个友好、高效的编程环境。无论是初学者还是经验丰富的开发者,都能从中受益...

    Android 计算器编写源代码

    接着,我们来看源代码的核心部分,即Activity的Java代码。这个Activity通常继承自`AppCompatActivity`,并实现与用户交互的逻辑。源代码中的Activity可能命名为`CalculatorActivity.java`。在这个类中,你需要找到对...

    VC6编写的科学计算器源代码

    从标签来看,“计算器”表示这是个进行数学计算的软件,“源代码”意味着我们能查看并修改程序的内部逻辑,“MFC”代表它使用的开发框架,“VC6”则明确了开发工具。 压缩包内的文件名列表提供了关于项目的一些具体...

    爱心网页-HTML-代码

    通过研究和修改代码,你可以提升对HTML语义的理解,提高CSS布局和美化的能力,以及增强JavaScript动态效果的编写技巧。 总的来说,"爱心网页-HTML-代码"是一个优秀的学习资源,它不仅展示了HTML、CSS和JavaScript的...

    数十个纯C写的游戏程序代码以及WORD文档说明

    从标签“游戏”来看,这些代码可能涵盖了各种类型的游戏,如文字冒险、迷宫、棋类、动作、射击等,每种游戏的实现都需要不同的算法和数据结构。学习这些代码可以深入了解游戏开发的基本流程,包括游戏循环、事件处理...

    C#编写的小游戏源代码

    从提供的文件名"Game"来看,这可能是一个包含整个游戏项目的文件夹或者是一个主程序文件。在C#游戏开发中,"Game"通常表示游戏的主类,它会包含游戏的初始化、更新循环、渲染以及与其他游戏组件的交互等关键功能。...

    程序员的代码的代码的代码

    首先,让我们来看看标签"程序员的代码的代码的代码"。这可能是对程序员编写高质量、可维护和可扩展的代码的挑战的一种象征。在实际开发中,代码通常需要具备良好的结构和清晰的逻辑,以便其他开发者理解和修改。因此...

    MATLAB高效编程程序源代码

    首先,我们来看看MATLAB的基本编程概念。MATLAB的编程基于向量和矩阵运算,这使得处理大量数据变得非常方便。源代码中可能会涵盖如何创建、修改和操作矩阵,以及如何利用循环、条件语句和函数进行控制流程。理解这些...

    职业代码_冒险岛代码_冒险岛_冒险岛脚本_stared7t3_冒险岛gm代码_

    首先,我们来看“职业代码”。在冒险岛游戏中,每个职业都有其特定的标识代码,这些代码用于区分不同的职业并进行相关的属性计算和技能处理。例如,“职业代码.txt”文件中可能包含了战士、弓箭手、法师等各类职业的...

    看代码软件notepad+

    它基于Windows操作系统,并且是开源的,这意味着它的源代码是公开的,任何人都可以查看、修改和改进。这款软件支持多种编程语言,包括但不限于C、C++、Java、Python、PHP、HTML、XML等,为程序员提供了极大的便利。 ...

    代码管家代码管家

    此外,从压缩包中的文件名“css3-text-water-loading”来看,我们可以推测“代码管家”可能包含了一些关于CSS3的技术,特别是针对文本的水印加载效果。CSS3是层叠样式表的最新版本,提供了许多新的选择器、动画和...

    Java工程师不得不看的几千份代码

    总的来说,这份"Java工程师不得不看的几千份代码"资源集是提升Java技能的宝贵财富,它覆盖了Java开发的各个方面,从基础到高级,从理论到实践,都为Java工程师提供了深入学习的机会。通过仔细研读和实践这些代码,...

    垃圾代码生成器

    垃圾代码生成器在这种情况下派上了用场,它能够自动生成大量代码,这些代码可能包含无意义的函数、变量和类,甚至可以模拟一些基本的UI交互,使应用看起来具有一定的功能性,但实际可能并无实质性用途。 使用【垃圾...

    用Java编写的状态模式代码,有重构前后的代码比较

    状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为,对象看起来似乎修改了它的类。这种模式常用于处理对象的状态变化,并且在不同的状态下,对象的行为也相应地变化。 首先,我们来理解状态模式的...

    编程中不得不看的牛逼代码

    5. **元编程**:编写能自我修改或生成代码的代码,这在某些场景下可以极大地提高效率,但同时也增加了代码的复杂性。 6. **并发与多线程**:在多核处理器时代,合理地利用并发和多线程可以显著提高程序性能,但同时...

    三个游戏内存修改工具源代码

    总的来说,通过研究这三种游戏内存修改工具的源代码,我们可以提升自己的编程技能,了解内存操作的底层原理,甚至可以自己创建更高效、更安全的内存修改工具。不过,应当注意的是,不正当使用这类工具可能违反游戏...

Global site tag (gtag.js) - Google Analytics