一、结对编程
- 陈凯欣:学号 201521123034+邱晓娴:学号 201521123037
- 码云地址:https://gitee.com/carolfantasy/software_engineering__pair_programming.git
二、需求分析
1、原项目分析
1.1项目类图
1.2存在的不足
- 变量的命名使用abc,不够规范。a既然表示操作符可以改名为oprator。
- 运行该程序,初次选择计算题数并进行计算,程序会将错题存在错题集中。之后所有的计算练习是在错题集中抽取。需要重新运行该程序才能选择新的题目进行练习。
- 复习错题选择的题数若超过错题集题数,未提示错误。
- 在做题界面中点击开始后修改计时时间,计时器停止运行,最后做题显示时间为手动修改的时间。
- 在做题界面点击开始后,再次点击开始,计时器重新计时。
- 错题集的题目根据每次做题的情况依次累加。若某题错误率高,随机选题复习的话很有可能选的题目都是该题,显得累赘。
1.3原项目测试用例
- (以整数和分数的乘法为例)原项目的单元测试分别对整数和分数的加减乘除进行测试,每个测试用了五个用例。
2、改进分析
2.1改善部分
- 修改的代码中加入多操作符的运算
- 加入支持括号优先级运算
- 对表达式进行去重
2.2思维导图
2.3支持括号优先级运算
2.4算式去重
- 暂时还没实现,但是写的算法大概如下:
int Isomorphic(Tree t1,Tree t2){ if((t1==NULL)&&(t2==NULL))return 1;//都为空 if(((t1==NULL)&&(t2!=NULL))||((t1!=NULL)&&(t2==NULL))) return 0;//一个为空另一个不为空 if(t1->root!=t2->root) return 0;//根节点的值不一样 if((t1->lchild==NULL)&&(t2->lchild==NULL)) return Isomorphic(t1->lchild,t2->rchild); if(((t1->lchild!=NULL)&&(t2->lchild!=NULL))&&(t1->lchild->data==t2->lchild->data))//没有交换 return (Isomorphic(t1->lchild,t2->lchild)&&Isomorphic(t1->rchild,t2->rchild));//如果两个都不为空且左儿子相等,应该递归的找左对应左,右对应右 else return (Isomorphic(t1->lchild,t2->rchild)&&Isomorphic(t1->rchild,t2->lchild));//否则就是交换了,递归的判断左对应右,右对应左 };
2.5测试用例
- 新功能测试用例
3、PSP表格
PSP2.1 | 个人开发流程 | 预估耗费时间(分钟) | 实际耗费时间(分钟) |
---|---|---|---|
Planning | 计划 | 8 | 15 |
·Estimate | 明确需求和其他相关因素,估计每个阶段的时间成本 | 8 | 6 |
Development | 开发 | 240 | 284 |
·Analysis | 需求分析 (包括学习新技术) | 6 | 10 |
·Design Spec | 生成设计文档 | 5 | 10 |
·Design Review | 设计复审 | 4 | 15 |
·Coding Standard | 代码规范 | 5 | 10 |
·Design | 具体设计 | 20 | 30 |
·Coding | 具体编码 | 288 | 300 |
·Code Review | 代码复审 | 20 | 30 |
·Test | 测试(自我测试,修改代码,提交修改) | 15 | 25 |
Reporting | 报告 | 9 | 10 |
· | 测试报告 | 3 | 5 |
· | 计算工作量 | 2 | 1 |
· | 并提出过程改进计划 | 3 | 3 |
4、附加题
4.1支持乘方运算
三、代码展示
BinaryTree类//构建二叉树 import java.util.ArrayList; public class BinaryTree { private TreeNode root; private int num; private ArrayList<TreeNode> opeList = new ArrayList<TreeNode>(); public BinaryTree(int num){ this.num = num; } public int getNum(){ return num; } public void setNum(int num){ this.num = num; } public void setTreeNode(TreeNode root){ this.root = root; } /** * 获取最终的表达式,必须在CalAndVal()方法后调用 * * @return str */ public String toString(){ String str = root.toString(); str = str.substring(1, str.length()-1); return str; } /** * 计算并验证表达式 * * @return result */ public String CalAndVal(){ return root.getResult(); } /** * 计算二叉树的深度(层数) * * @return deep */ public int getDeep(){ int i = this.num; int deep = 2; while(i/2 > 0){ deep++; i /= 2; } return deep; } /** * 生成二叉树 * */ public void createBTree(){ TreeNode lchild, rchild, lnode, rnode; int ran= Ran.getNumber(2),x,y; if(num == 1){ if(ran==0){ lchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); } else{ x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } lchild = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } rchild = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); } root = new TreeNode(String.valueOf(Ran.getOperator()), lchild, rchild); } else{ int num1 = 0; int n = getDeep() - 3; boolean[] place = Ran.getChildPlace(num); root = new TreeNode(String.valueOf(Ran.getOperator()), null, null); opeList.add(root); for(int i = 0; i < n; i++){ for(int j = 0; j < (int)Math.pow(2, i); j++, num1++){ lchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null); rchild = new TreeNode(String.valueOf(Ran.getOperator()), null, null); opeList.get(j + num1).setChild(lchild, rchild); opeList.add(lchild); opeList.add(rchild); } } for(int i = 0; i < place.length; i++){ if(place[i]){ if(ran==0){ lnode = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); rnode = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); } else{ x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } lnode = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } rnode = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); } if(i%2 == 0){ lchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode); opeList.add(lchild); opeList.get(num1).setLchild(lchild); } else{ rchild = new TreeNode(String.valueOf(Ran.getOperator()), lnode, rnode); opeList.add(rchild); opeList.get(num1).setRchild(rchild); } } else{ if(i%2 == 0){ if(ran==0){ lchild=new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); } else{ x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } lchild = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); } opeList.get(num1).setLchild(lchild); } else{ if(ran==0){ rchild = new TreeNode(String.valueOf(Ran.getNumber(10)), null, null); } else{ x=Ran.getNumber(9)+1; y=Ran.getNumber(20)+1; for(int j=0;y<=x;j++){ y=Ran.getNumber(20)+1; } rchild = new TreeNode(String.valueOf(x)+"/"+String.valueOf(y), null, null); } opeList.get(num1).setRchild(rchild); } num1 = num1 + i%2; } } } } }
TreeNode类//树节点类,并进行递归计算 public class TreeNode { private String str; private TreeNode rchild = null; private TreeNode lchild = null; public TreeNode(String str){ this.str = str; } public TreeNode(String str, TreeNode lchild, TreeNode rchild){ this.str = str; this.rchild = rchild; this.lchild = lchild; } public void setChild(TreeNode lchild, TreeNode rchild){ this.lchild = lchild; this.rchild = rchild; } public TreeNode getRchild() { return rchild; } public void setRchild(TreeNode rchild) { this.rchild = rchild; } public TreeNode getLchild() { return lchild; } public void setLchild(TreeNode lchild) { this.lchild = lchild; } public String getStr(){ return str; } /** * 获取每个节点的运算结果,并检验除法 * 1)除数为0 * 2)不能整除 //不考虑,允许分数的出现 * 出现以上两种情况的话将该运算符转换成其他三种运算符 * * @return result */ public String getResult(){ Compute com=new Compute(); if(hasChild()){ switch(str){ case "+": if(!getLchild().getResult().contains("/")) return String.valueOf(Integer.parseInt(getLchild().getResult()) + Integer.parseInt(getRchild().getResult())); else return com.Add(getLchild().getResult(),getRchild().getResult()); case "-": if(!getLchild().getResult().contains("/")) return String.valueOf(Integer.parseInt(getLchild().getResult()) - Integer.parseInt(getRchild().getResult())); else return com.Subtract(getLchild().getResult(),getRchild().getResult()); case "*": if(!getLchild().getResult().contains("/")) return String.valueOf(Integer.parseInt(getLchild().getResult()) * Integer.parseInt(getRchild().getResult())); else return com.Multiply(getLchild().getResult(),getRchild().getResult()); case "÷": if(getRchild().getResult().equals("0")||getRchild().getResult().contains("0/")){ while(str.equals("÷")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } else{ if(!getLchild().getResult().contains("/")){ if(Integer.parseInt(getLchild().getResult()) % Integer.parseInt(getRchild().getResult()) != 0){ while(str.equals("÷")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } else return String.valueOf(Integer.parseInt(getLchild().getResult()) / Integer.parseInt(getRchild().getResult())); } else{ return com.Divide(getLchild().getResult(),getRchild().getResult()); } } case "^": if(!getLchild().getResult().contains("/")){ if(Math.pow(Double.parseDouble(getLchild().getResult()),Double.parseDouble(getRchild().getResult()))<1){ while(str.equals("^")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } else return String.valueOf((new Double (Math.pow(Double.parseDouble(getLchild().getResult()),Double.parseDouble(getRchild().getResult())))).intValue()); } else{ while(str.equals("^")){ str = String.valueOf(Ran.getOperator()); } return this.getResult(); } } } return str; } /** * 先对每个运算式添加括号,然后根据去括号法则,去掉多余的子式的括号 * * @return string */ public String toString(){ String Lstr = "", Rstr = "", Str = ""; if(hasChild()){ //右子树如果有孩子,说明右子树是一个表达式,而不是数字节点。 if(getRchild().hasChild()){ //判断左邻括号的运算符是否为'/' if(str.equals("÷")||str.equals("^")){ //获取右子树的表达式,保留括号 Rstr = getRchild().toString(); } //判断左邻括号的运算符是否为'*'或'-' else if(str.equals("*") || str.equals("-")){ //判断op是否为'+'或'-' if(getRchild().str.equals("+") || getRchild().str.equals("-")){ Rstr = getRchild().toString(); } else{ //获取右子树的表达式,并且去括号 Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1); } } else{ //右子树除此之外都是可以去括号的。 Rstr = getRchild().toString().substring(1, getRchild().toString().length()-1); } } else{ Rstr = getRchild().str; } //左子树的情况同右子树类似 if(getLchild().hasChild()){ if(str.equals("*") || str.equals("÷")){ if(getLchild().str.equals("+") || getLchild().str.equals("-")){ Lstr = getLchild().toString(); } else{ Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1); } } else{ Lstr = getLchild().toString().substring(1, getLchild().toString().length()-1); } } else{ Lstr = getLchild().str; } //获取当前的运算式,并加上括号 Str = "(" + Lstr + str + Rstr + ")"; } else{ //若没有孩子,说明是数字节点,直接返回数字 Str = str; } return Str; } public boolean hasChild(){ if(lchild == null && rchild == null) return false; else return true; } }
Compute类//对分数进行计算 public class Compute { public String Add(String str1,String str2){ int mol1,mol2,den1,den2; int mol,den; mol1=Integer.parseInt(Trans(str1)[0]); den1=Integer.parseInt(Trans(str1)[1]); mol2=Integer.parseInt(Trans(str2)[0]); den2=Integer.parseInt(Trans(str2)[1]); mol=mol1*den2+mol2*den1; den=den1*den2; int gcd=Compute.GCD(mol,den); mol/=gcd; den/=gcd; return(mol+"/"+den); } public String Subtract(String str1,String str2){ int mol1,mol2,den1,den2; int mol,den; mol1=Integer.parseInt(Trans(str1)[0]); den1=Integer.parseInt(Trans(str1)[1]); mol2=Integer.parseInt(Trans(str2)[0]); den2=Integer.parseInt(Trans(str2)[1]); mol=mol1*den2-mol2*den1; den=den1*den2; int gcd=Compute.GCD(mol,den); mol/=gcd; den/=gcd; return(mol+"/"+den); } public String Multiply(String str1,String str2){ int mol1,mol2,den1,den2; int mol,den; mol1=Integer.parseInt(Trans(str1)[0]); den1=Integer.parseInt(Trans(str1)[1]); mol2=Integer.parseInt(Trans(str2)[0]); den2=Integer.parseInt(Trans(str2)[1]); mol=mol1*mol2; den=den1*den2; int gcd=Compute.GCD(mol,den); mol/=gcd; den/=gcd; return(mol+"/"+den); } public String Divide(String str1,String str2){ int mol1,mol2,den1,den2; int mol,den; mol1=Integer.parseInt(Trans(str1)[0]); den1=Integer.parseInt(Trans(str1)[1]); mol2=Integer.parseInt(Trans(str2)[0]); den2=Integer.parseInt(Trans(str2)[1]); mol=mol1*den2; den=den1*mol2; int gcd=Compute.GCD(mol,den); mol/=gcd; den/=gcd; return(mol+"/"+den); } public static String[] Trans(String str){ String[] strArray=new String[2]; for(int i=0;i<str.length();i++){ if(str.charAt(i)=='/'){ strArray[0]=str.substring(0,i); strArray[1]=str.substring(i+1,str.length()); break; } } return strArray; } public static int GCD(int a,int b) { if(b==0) return a; else return GCD(b,a%b); } }
QA_List类//输出界面根据题数调用式子及返回结果 import java.util.ArrayList; import java.util.List; import java.util.Random; public class QA_List { public static int i; public static List<String> Qusetion=new ArrayList<String>(); public static List<String> Answer=new ArrayList<String>(); public QA_List(){ for(int a=0;a<i;a++) { BinaryTree bTree; bTree = new BinaryTree(2);//随机生成数与表达式的数字个数有关 bTree.createBTree(); String result = bTree.CalAndVal(); if(result.contains("/")){ if(Compute.Trans(result)[0].equals("0")){ result="0"; }else if(Compute.Trans(result)[1].equals("1")){ result=Compute.Trans(result)[0]; } } Answer.add(result); Qusetion.add(bTree.toString()); } for(int a=0;a<(10-i);a++){ Answer.add(""); Qusetion.add(""); } } }
四、程序运行
五、小结感受
1、结对照片
2、结对编程真的能够带来1+1>2的效果吗?通过这次结对编程,请谈谈你的感受和体会。
我觉得结对编程确实有1+1>2的效果。编程过程有时候出现的bug怎么找都找不到,都说旁观者清,说不定在小伙伴的检查下能够有意想不到的收获;编程的整个算法思路能够有人一起沟通,可以择较优者进行编程,会事半功倍。遇到困难时,有个人能够互相勉励,还是挺不错的。
3、码云提交记录截图
来源:https://www.cnblogs.com/qxx-Ultraman/p/8641943.html