首页 > 技术文章 > 软件工程第三周 结对编程

ycll 2018-03-21 09:42 原文

一、链接

二、代码

1. 需求分析:

  • 现有代码实现简单的加减乘除算术出题运算,提供三种语言的切换,有复习,计时,判断正确率的功能
  • 对现有代码进行分析,发现如下问题:
    • (1)复习功能不完善,复习的按钮在初始界面,点击之后跳回初始界面,无法再进行下一步的点击
    • (2)初始界面较为粗糙,界面出现在屏幕左上角,用起来很难受。语言切换非常不明显,整体布局也不太符合人体习惯
    • (3)文档作用不明显,对于源代码的分析了解基本是我们从代码直接进行分析的。变量名设置比较随意,一些重要框架没有说清楚。
    • (4)因为软件面向的对象定为小学生,所以两个数的加减乘除的数取值在100以内,混合运算的取值则在10以内,相对符合现在学生情况。
    • (5)原本的“再一次”和“复习”功能存在bug,重新进行选择不会读取用户再一次输入的题目数量。另外“复习”部分只会弹出当此的题目,而不会从错题库中随机读取,比较不符合练习题的规范。
    • (6)未对重复题目的出现进行判断
  • 进行优化的部分:
    • 更改初始界面,初始界面增加“复习”部分,语言选择改为菜单栏选项。在不对界面框架做大的更改的情况下,增加按键,修改布局,界面显示在电脑屏幕正中间。
    • 优化界面显示和跳转,在第二个界面加上“结束”部分,优化用户手感问题。同时添加跳出所有正确答案功能,在希望结束的时候不用去点击右上角的,而是可以自己选择结束暂停。
    • 新增平方和括号的计算,并在原来只针对分数的加减乘除的计算题扩大范围。
    • 关于计算部分的优化,经过一定的搜索,现在的小学生混合运算主要偏向于能够快速化简的分数除法,我们的代码还暂时无法达到这个要求,这个也需要进一步的升级。
    • 优化上述第五点的问题,实现了一个较为正常的“复习”功能和“再一次”的选择
    • 优化上述第六点的问题,解决了对于历史题目重复出题的bug。但是对于一开始随机的出题的重复率判断,有尝试解决,结果发现了源代码更大的bug。在解决重复出题的过程中,这些bug都一一解决了,然而这个最开始的问题......还是凉了。详情见队友博客。

2. 程序设计:

2.1 码云提交记录

2.2 类图部分


2.3 覆盖率测试

其中覆盖率偏低的几个类,都存在或多或少的测试局限问题,比如下图,分母等于0的概率实在太低,太难触发

这部分是关于做题时长的测试,当做题时间过长才会出现

还有这里是对于历史存储的问题,因为有原文件的存在,所以没有触发

另外大部分的问题都在于代码的异常捕获,这个......真的挺难触发的

2.4 逻辑图部分,红色的表示我们做了优化或是新增的功能

2.5 内存分析优化



3. 代码展示:

public String fra_operation(){	//复杂运算
		
		this.t1 = new Random().nextInt(10)%(10-1+1) + 1;
		this.t2 = new Random().nextInt(10)%(10-2+1) + 2;
		this.t3 = new Random().nextInt(10)%(10-1+1) + 1;
		this.t4 = new Random().nextInt(10)%(10-2+1) + 2;
		int fz=1,fm=t2*t4;	//分子,分母
		
		if(k==1)//除法
		{
			if(t2<t1||t4<t3||t2%t1==0||t4%t3==0)
			{
				astr=fra_operation();
				return astr;
			}		
			if(k1==0)
			{
				fz=t1*t4+t2*t3;
			}
			
			if(k1==1){
				fz=t1*t4-t2*t3;
				if(fz==0)
				{
					return astr=("0");
				}
			}
			
			if(k1==2)
				fz=t1*t3;
			if(k1==3)
			{
				fz=t1*t4;
				fm=t2*t3;
			}
			int f=common_divisor(fm,fz);
		
			if(f>0){
				fm=fm/f;
				fz=fz/f;
			}
			if(f<0){
				fm=-fm/f;
				fz=-fz/f;
			}
			astr = (fz+"/"+fm);
			if(astr.equals("1/1")) astr="1";
			if(fm==1) astr = (fz+"");	
		}
		else if(k==0)//乘法
		{
			fm=1;
			if(k1==0)//加法
			{
				if(k2==3)
					fz=t1*(t2+t3)*t4;
				else fz=t1*t2+t3*t4;
			}
			if(k1==1)//减法
			{
				if(k2==3)
					fz=t1*(t2-t3)*t4;
				else fz=t1*t2-t3*t4;
			}
			if(k1==2)//乘法
			{
				fz=t1*t2*t3*t4;	
			}	
			if(k1==3)//除法
			{

				if(k2==1)//t1*t2/(t3*t4)
				{
					if(t1==0||t2==0) return astr=("0");
					else if(t3==0||t4==0)
					{
						astr=fra_operation();
						return astr;						
					}
					else{
						fz=t1*t2;
						fm=t3*t4;
					}
				}
				
				else	//if(k2==0) t1*t2/t3*t4
				{
					if(t3==0)
					{
						astr=fra_operation();
						return astr;
					}
					else if(t1==0||t2==0||t4==0) return astr=("0");
					else{
						fz=t1*t2*t4;
						fm=t3;	
					}
				}			
				
				int f=common_divisor(fm,fz);
						
				if(f>0){
					fm=fm/f;
					fz=fz/f;
				}
				if(f<0){
					fm=-fm/f;
					fz=-fz/f;
				}
				astr = (fz+"/"+fm);
			}	
			if(k1!=3) astr=fz+"";
			if(fm==1) astr = (fz+"");
		}
		if(astr.equals("1/1")) astr="1";	//修改 bug	1
		return astr;	
	}
	
	//复习键
    private void Re_ButtonMouseClicked(MouseEvent evt) throws FileNotFoundException { 
    	if(Number.getText().matches("\\d*")){
    		Frame.hh.i = Integer.parseInt(Number.getText());	//获取输入题数
    		if(Frame.hh.i<=10&&Frame.hh.i>=1)
    		{  			
	        	Frame.hs.Histroy_read();
	            for(int a=0;a<Frame.hh.i;a++){   	
	            	int b = new Random().nextInt(Frame.hs.astrlist.size()-1);
	            	Frame.hh.Answer.set(a, Frame.hs.astrlist.get(b));
	            	Frame.hh.Qusetion.set(a, Frame.hs.qstrlist.get(b));
	            	List<Integer> count=new ArrayList<Integer>(Arrays.asList(list));
	            	if(count.contains(b)) a--;
	            	else list[a]=b;	
	            }
	            for(int a=0;a<(10-Frame.hh.i);a++){
	            	Frame.hh.Answer.set(a+Frame.hh.i, "");
	            	Frame.hh.Qusetion.set(a+Frame.hh.i, "");
	               }
	        }
    			
	        java.awt.EventQueue.invokeLater(new Runnable() {
	              public void run() {
	                   new Frame(true).setVisible(true);
	               }
	           });	
	        	this.dispose();
	        	i++;
	        	try {
	    			Frame.hs.History_num();
	    		} catch (FileNotFoundException e) {
	    			e.printStackTrace();
	    		}
    		}	
    } 

public class QA_List {

	public static int i=0;
	public static List<String> Qusetion;
	public static List<String> Answer;
	public QA_List(){	
		Qusetion=new ArrayList<String>();
		Answer=new ArrayList<String>();
		
		for(int a=0;a<i;a++)
	    {			
			Runtime.getRuntime().gc();		
	        boolean flag= new Random().nextBoolean();        
	        Arithmetic hh = new Arithmetic(flag);	        
	        String int_str = hh.int_operation();
	        String fra_str = hh.fra_operation();	        
	        if(flag==true)	//
	        {
	        	Answer.add(int_str);
	        	Qusetion.add(hh.toString());
	        }	        	
	        if(flag==false)	//
	        {
	        	Answer.add(fra_str);
	        	Qusetion.add(hh.toString());
	        }
	        System.out.println(Answer.get(a)+"   "+Qusetion.get(a));
	    }		
     for(int a=0;a<(10-i);a++){
     	Answer.add("");
     	Qusetion.add("");
     }
	}	 
}

4. 程序运行:

  • 由于在开发过程中我不做结果正确率的测试,所以截图里的正确率偏低。






5. 小结感受:

  • 先说一点,,,我认为开发工具的统一十分重要。就比如我们使用的源代码的图形界面时在netbeans的环境下直接拖选的,netbeans会生成大量的无用代码以及框架比较难以更改。在此基础上,手动修改的难度就比较大。
  • 回到正题,我认为结对编程可以带来1+1>2的效果。首先,每个人擅长的方向不一样,一个软件的开发是需要各方各面的功能的。一个人很难做到面面精通,所以通过合作,可以让开发效果,从技术水平上来说达到最好。
  • 另外,一个人的思路总是狭窄的。比起两个人一起,一个人更容易钻牛角尖。比如我在调试一个界面的位置问题时,总是没法调整到想要的地方,也查不到能用的函数,队友换了个关键词一下子就查到了。我简直怀疑她和我用的不是同一个百度。就,总的来说,很多时候有个能讨论的人,在开发的时候不会太过于局限,不管是思路还是设计都会开阔很多。
  • 在原本的代码框架上进行修改相对困难,但是好处是基础都有,只要优化就行了,不过也有不少功能需要把之前的方法推翻重来。
  • 还有一点就是,结对编程对于水平稍微弱一点的人来说,其实是很好的。分工合作愉快
  • 队友身体不舒服一周了,挺艰难的
  • 以及......在思考的时候被打断,是真的需要全部重新思考的......我们深刻体会了这个道理


(亲身体验,真的)

三、 结对编程照片

由于两人非同宿舍,图书馆讨论不方便。再由于我们这一次的编程周期偏长,基本用了一周半的时间,时间也不能完全对的上,所以我们都是在QQ上互相交流和讨论的。比如我在外面的时候,她在编程,遇到的问题和一些地方都会提出来一起讨论。所以要两人非摆拍的一起讨论的照片......那是没有的。我们只提供得了聊天记录截图......(内容有点多......)


以上是我们针对一个按钮的新功能的测试阶段的部分讨论

四、 PSP部分

推荐阅读