结对编程——队友个人项目代码分析

一、 项目需求

(一)项目名称:中小学数学卷子自动生成程序

(二)面向用户:小学、初中和高中数学老师

(三)功能:

1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;

2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;

3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);

4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;

5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行。

附表-1:账户、密码

账户类型

账户

密码

备注

小学

张三1

123

张三2

123

张三3

123

初中

李四1

123

李四2

123

李四3

123

高中

王五1

123

王五2

123

王五3

123

附表-2:小学、初中、高中题目难度要求

小学

初中

高中

难度要求

+,-,*./

平方,开根号

sin,cos,tan

备注

只能有+,-,*./和()

题目中至少有一个平方或开根号的运算符

题目中至少有一个sin,cos或tan运算符

 

二、 代码分析

1、 整体分析:按照功能需求与程序执行步骤将功能逐个实现,功能比较完整。

2、 各函数分析

(1)相关类的成员变量         

        Scanner scan=new Scanner(System.in);
	Random random=new Random();
	String m_username="",m_password=""; 
	String m_difficulty=""; 
	int m_titlesum; 
	String[] m_nums=new String[5];//每个操作数及其围绕操作符总体
	String[] m_title=new String[30];
	char[] m_operator= {'+','-','*','/'};
	    

    优点:各变量含义一目了然。

    缺点:对于用户的相关属性,抽象为一个用户类再定义相关字段可能会更好。

(2) 登陆函数

public void LoginAccount()
	{
		while(true)
            {
			for(int i=0;i<30;i++)
			{
				m_title[i]="";
			}
			if(m_username=="")
			{
				System.out.print("请输入用户名、密码:");
			}
			m_username=(scan.next()).trim();
        	     m_password=(scan.next()).trim();
        	     scan.nextLine();
        	
        	     if(("张三1".equals(m_username)||"张三2".equals(m_username)||"张三3".equals(m_username)||
        	        "李四1".equals(m_username)||"李四2".equals(m_username)||"李四3".equals(m_username)||
        	        "王五1".equals(m_username)||"王五2".equals(m_username)||"王五3".equals(m_username))
        		 &&"123".equals(m_password))   //输入正确的话,确定难度以及判断是否会改变类型   	
        	     {
         		  if("张三".equals(m_username.substring(0,2)))
        		  {
        			m_difficulty="小学";
        		  }
        		  else if("李四".equals(m_username.substring(0,2)))
        		  {
        			m_difficulty="初中";
        		  }
        		  else if("王五".equals(m_username.substring(0,2)))
        		  {
        			m_difficulty="高中";
        		  }
        		  System.out.println("当前选择为"+m_difficulty+"出题");
        		
        		  System.out.print("是否切换类型(若不切换请回车跳过,切换请输入切换类型):");
        		  String str="";      //判断是否切换题目难度类型
        		  str=scan.nextLine();	
        		  if(str.length()!=0) //需改变类型
        		  {
        			int count=0;
        			while(true) //确保输入正确
        			{
        				count++;
        				if(count!=1)
        				{
        					str=scan.next();
        				}	
        				if("切换为小学".equals(str)||"切换为初中".equals(str)||"切换为高中".equals(str))
            			{
        					m_difficulty=str.substring(3);
        					break;//输入正确后跳出该确保输入正确循环
            			}
        				else//否则提示直到输入正确
        				{
        					System.out.println("请输入小学、初中和高中三个选项中的一个");
        				}
        			}	
        		}
        		
        		break; //输入正确,跳出循环
        	}
        	else //否则系统提示,循环直到密码输入正确
        	{
        		System.out.println("请输入正确的用户名、密码");
        	}
        }
		
	IsLogout(); 
}

   分析:代码注释很多,但是从代码长度来看过于冗杂,且代码过长可读性也不好。该函数的功能主要是进行用户登陆并确定试卷类型,与此同时通过判断输入的是否是回车来确认用户是否想要切换试卷类型。但判断输入用户的信息是否正确的if语句过长,可改进为针对不同用户来设定相应的用户信息核对函数等。关于字符串相等的判断也过于反复,可简化为设定成一个简易的判断字符串相等的函数。函数的功能也过多导致代码过长,可考虑分化成两个方法去实现。

 (3) IsLogout函数  

/*输入题目数量或退出当前账户*/
	public void IsLogout()
	{
		System.out.print("准备生成"+m_difficulty+"数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");

		while(true)
		{
			m_titlesum=scan.nextInt();
			if(m_titlesum==-1) //若退出则重新登陆
			{
				m_username="";
				m_password="";
				LoginAccount();
				break;
			}
			else if(m_titlesum<10 || m_titlesum>30)
			{
				System.out.print("题目数量的有效输入范围是“10-30”(含10,30),请输入生成题目数量(输入-1将退出当前用户,重新登录):");	
			}
			else
			{
				CreateFile();
			}
		}
	}
	

     分析:该函数功能主要是获取生成试卷的题目数量,并判断是否退出当前用户,若退出则返回至登陆函数,否则获取题目数量后生成卷子。

(4) 出卷子(创建文件)

public void CreateFile()
	{
		String path=m_username+"\\"+m_difficulty;
		File file=new File(path); //在相应路径下创建目录
		
		if(!file.exists()) //若文件夹不存在,则新建
		{
			file.mkdirs();
		}
		
		for(int i=0;i<m_titlesum;i++)
		{
			do
			{
				int n=random.nextInt(4)+2;//确定操作数n,2-5
				int operatorsum=n-1; //确定运算符
				
				int[] num=new int[n];//操作数集合
				for(int j=0;j<n;j++)
				{
					num[j]=random.nextInt(100)+1;//操作数范围1-100
					m_nums[j]=Integer.toString(num[j]);
				}

				char[] opers=new char[operatorsum];//随机出的运算符集合
				for(int j=0;j<n-1;j++)
	        	    {
	        		  opers[j]=m_operator[random.nextInt(4)];
	        	    }
				
				if("小学".equals(m_difficulty))
				{
					SetPriQuestion(n);
				}
				else if("初中".equals(m_difficulty))
				{
					SetJunQuestion(n,num);
				}
				else
				{
					SetHighQuestion(n,num);
				}
				
				for(int j=0;j<n;j++)
	       	           {
	       		         m_title[i]+=m_nums[j];
	       		         if(j<n-1)
	       		         {
	       			     m_title[i]+=opers[j];
	       	                }
	       	            }
			}
			while(Check(m_title[i])==0);	
		}
		
		try
		{
			//创建文件并以当前系统时间命名
		    BufferedWriter bw=new BufferedWriter(new FileWriter(path+"\\"+(new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")).format(new Date())+".txt"));
	            for(int k=0;k<m_titlesum;k++)
	            {
	        	  bw.write(k+1+"、"+m_title[k]+"="+"\r\n\r\n");
	            }
			bw.close();
		}
		catch(Exception e) 
		{
			e.printStackTrace();
		}
	    System.out.print("卷子生成成功!");
	}
	

     分析:该函数的功能是首先创建目录,再根据选择的试卷类型来出相应的题目。但文件是创建在E盘下,为便于观察,可考虑将文件创建在当前目录,对于程序用户也便于观察。此处还可看出题目的思路是,将一道题分为操作数及运算符两部分组成,操作数个数、操作数以及运算符随机,但一个操作数可依附有其相应符号如平方、根号、三角函数运算符以及括号等。最后创建文件并将内容写入。想法新奇但可能有点过于复杂,可考虑更简便的出题思路与方法。

(5) 设置小学题目

public void SetPriQuestion(int n)
	{
		if(n==3) //若有三个操作数,则括号有两种可能
    	{
    		switch(random.nextInt(2))//随机
    		{
    		    case 0:
		             m_nums[0]="("+m_nums[0];
		             m_nums[1]+=")";
			     break;
		    case 1:
			     m_nums[1]="("+m_nums[1];
			     m_nums[2]+=")";
			     break;
    		 };	 
    	}
		else if(n==4)
		{
		    switch(random.nextInt(5))
		    {
		       case 0:
			    m_nums[0]="("+m_nums[0];
			    m_nums[1]+=")";
			    break;
		       case 1:
		    	    m_nums[1]="("+m_nums[1];
		            m_nums[2]+=")";
		            break;
		       case 2:
	    	          m_nums[2]="("+m_nums[2];
		            m_nums[3]+=")";
		            break;
		       case 3:
	    	            m_nums[0]="("+m_nums[0];
		            m_nums[2]+=")";
		            break;
		       case 4:
	    	           m_nums[1]="("+m_nums[1];
	                   m_nums[3]+=")";
	                   break;
		   }

		}
		else if(n==5)
		{
		  switch(random.nextInt(9))
		   {
		     case 0:
			    m_nums[0]="("+m_nums[0];
			    m_nums[1]+=")";
			    break;
		       case 1:
		    	    m_nums[1]="("+m_nums[1];
		            m_nums[2]+=")";
		            break;
		       case 2:
	    	           m_nums[2]="("+m_nums[2];
		            m_nums[3]+=")";
		            break;
		       case 3:
	    	          m_nums[0]="("+m_nums[0];
		            m_nums[2]+=")";
		            break;
		       case 4:
	    	          m_nums[1]="("+m_nums[1];
	                  m_nums[3]+=")";
	                  break;
		       case 5:
		        m_nums[3]="("+m_nums[3];
			    m_nums[4]+=")";
			    break;
		       case 6:
			    m_nums[2]="("+m_nums[2];
			    m_nums[4]+=")";
			    break;
		       case 7:
			    m_nums[0]="("+m_nums[0];
			    m_nums[3]+=")";
			    break;
                case 8:
			    m_nums[1]="("+m_nums[1];
			    m_nums[4]+=")";
			    break;
		   }
		}
	}
	

    分析:该函数是设置小学题目。此处主要是设置题目中的括号安置,对于括号的安置情况主要是穷举了所有情况才进行随机选择,但多种情况的列举有点过于繁琐,且最多只能生成一个括号,可考虑用更优化的算法来生成括号。

(6) 设置初中题目

public void SetJunQuestion(int n,int num[])
	{
		double result;
		int count=0;
		for(int j=0;j<n;j++)
		{
			result=Math.sqrt(num[j]);
			if(result%1==0) //若易开方,则配以开方运算符
			{
				if(random.nextInt(2)==1)
				{
					m_nums[j]="(√"+m_nums[j]+")";
					count++;
				}
			}
			if(random.nextInt(2)==1)
			{
				m_nums[j]+="^2";
				count++;
			}
		}
		if(count==0)//确保至少一个开方或平方运算
		{
			int fla=random.nextInt(n);
			result=Math.sqrt(num[fla]);
			if(result%1==0)
			{
				m_nums[fla]="(√"+m_nums[fla]+")";
			}
			else
			{
				m_nums[fla]+="^2";
			}
		}		
	}

   分析:该函数主要设置题目中的平方或开方运算。优点是考虑了初中题目难度,如开方会考虑结果是否是整数。

(7) 设置高中题目

public void SetHighQuestion(int n,int num[])
	{
		String[] high= {"sin","cos","tan"};
	
		int[] ready=new int[] {30,45,60,90,120,135,150,180};//特殊三角函数值
		int[] ready2=new int[] {30,45,60,180};
		int count=0;
		
		for(int j=0;j<n;j++)
		{
			int tempflag=random.nextInt();
			if(random.nextInt(4)!=3)
			{
				String symbol=high[random.nextInt(3)];
				if(symbol=="tan")
				{
					m_nums[j]=Integer.toString(ready2[random.nextInt(4)]);
				}
				else
				{
					m_nums[j]=Integer.toString(ready[random.nextInt(7)]);
				}
				m_nums[j]=symbol+m_nums[j]+"°";
				count++;
			}
		}
		if(count==0)
		{
			int fla=random.nextInt(n);
			m_nums[fla]=high[random.nextInt(3)]+m_nums[fla]+"°";
		}
	}

    分析:该函数主要设置高中题目中的三角函数运算符功能。优点是考虑了高中题目难度,为三角函数值分配了特殊值。

(8) 查重

	public int Check(String str) //查当前账户文件下是否有重复题目
	{
		int flag=1;
		String[] nam=new String[] {"小学","初中","高中"};
		for(int i=0;i<3;i++)
		{
			File file=new File("E:\\"+m_username+"\\"+nam[i]);
			if(file.exists())
			{
				File[] files=file.listFiles();
				for(File f:files)
				{
					ArrayList<String> t=new ArrayList<String>();
			             BufferedReader br=null;
			        try 
			        {
			        	br=new BufferedReader(new FileReader("E:\\"+m_username+"\\"+nam[i]+"\\"+f.getName()));
			        	String s=br.readLine();
			        	while(s!=null) 
			        	{
			        		if(s.indexOf("、")!=-1)
			        		{
			        			if(s.substring(s.indexOf("、")+1).equals(str))
				        		{
				        			flag=0;
				        			break;
				        		}
			        		}
			        		s=br.readLine();			        		
			        	}
			        	if(flag==0)
			        	{
			        		break;
			        	}
			        }
			        catch(IOException e) 
			        {
			        	e.printStackTrace();
			        }
			        finally 
			        {
			        	try 
			        	{
			        		if(br!=null)
			        		{
			            		    br.close();
			            	     }
			        	}
			        	catch(IOException e) 
			        	{
			            	  e.printStackTrace();
			        	}
			        }
				}
				
				if(flag==0)
				{
					break;
				}			
		   }
			
		}
		return flag;
	}

  分析:该函数主要是当每随机生成一个题目时,会将其与当前账户所出过的所有题目进行比较是否重复。

(9) 主函数

public static void main(String[] args) {
         Project project=new Project();
         project.LoginAccount(); //从登陆账户开始
	} 

    分析:在主函数中直接从登陆账户开始。虽过程会随着方法进行而明了,但在主函数中未能更直接地体现对象化思想。 

三、 功能验证

 四、 整体总结

  功能都基本实现了,但还存在一些细节没有考虑到,例如小学题目中的多括号叠加的情况以及当前文件下的题目不能重复等。且编码风格不太符合java中的面向对象的思想,一些代码也过于冗长且算法过于复杂,还有改进的空间。

猜你喜欢

转载自www.cnblogs.com/gorgor2ls/p/11552681.html