项目成员:林楚虹 201521123002 + 林艺如 201521123004
码云地址:https://gitee.com/dabao_lyr/software_engineering
1. 改进现有代码
-
分析网络14部分现有程序代码(请选择其中一个)
选择:个人博客地址5:http://www.cnblogs.com/NianQiFeng ,源代码:https://git.coding.net/qwx_hh/java-szys.git -
逻辑泥球
原代码在构建错题复习的功能中,无法进入到文件中读取错题。 -
重构
1.clone项目,在开发环境中运行该应用程序,修改完毕最后上传至码云,并在博客中给出链接
https://gitee.com/dabao_lyr/software_engineering
代码规范也已上传
2.检查大部分主要类之间的关系,画出类图
图片来源:http://www.cnblogs.com/NianQiFeng/p/6551489.html
3.代码覆盖率
4.当前程序是否支持多个操作符运算,如果不支持,请在源代码基础上进行改进,算法参考:https://www.cnblogs.com/dragondove/p/6445850.html
支持多个操作符运算
public String int_operation()
{
int result = 0;
if(a==0)
result=f+g;
if(a==1)
result=f-g;
if(a==2)
result=f*g;
astr = String.valueOf( result);
if(a==3)
{
if(g==0)
{
astr=int_operation();
return astr;
}
else
{
if(g!=0&&g!=1){
int d=common_divisor(f,g);
f=f/d;
g=g/d;
astr = (f+"/"+g);
}
if(g==1)
astr=(""+f);
}
}
return astr;
}
public String fra_operation(){
this.b = new Random().nextInt(10)%(10-1+1) + 1;
this.c = new Random().nextInt(10)%(10-2+1) + 2;
this.d = new Random().nextInt(10)%(10-1+1) + 1;
this.e = new Random().nextInt(10)%(10-2+1) + 2;
if(c<b||e<d||c%b==0||e%d==0)
{
astr=fra_operation();
return astr;
}
int fz=1,fm=c*e;
if(a==0)
fz=b*e+c*d;
if(a==1){
fz=b*e-c*d;
if(fz==0)
{
return astr=("0");
}
}
if(a==2)
fz=b*d;
if(a==3)
{
fz=b*e;
fm=c*d;
}
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);
return astr;
}
5.单元测试
- 参考
重构-靠谱程序员的必备技能:https://mp.weixin.qq.com/s/23a8BY_fP168GWLrGLJzrw
JUnit单元测试:http://www.cnblogs.com/happyzm/p/6482886.html
Java覆盖率统计:http://www.cnblogs.com/happyzm/p/6530384.html
2. 功能改进与扩展
增加一个运算符,程序应该有怎样的改变?不得不扔掉全部重写么,还是可以只改部分模块?基于模块化设计的思想,考虑在现有程序中做什么样的修改,才能让程序更好地实现新的需求
-
需求分析
原先的程序仅仅支持简单的四则运算,不能出带括号的题目以及会出现重复的题目,这远远不能满足数学教学要求,所以添加了带括号的操作算式,以及保证了不会重复的题目,使得使用者可以做更多不同题目。 -
设计分析
-
增加括号操作符
public String ns()
{
int a=new Random().nextInt(2);
int k = new Random().nextInt(100);
int m = new Random().nextInt(100);
int n=new Random().nextInt(4);
String qstr = null;
if(a==0)
qstr=String.valueOf(new Random().nextInt(100));
else{
if(n==0)
qstr="("+k+"+"+m+")";
if(n==1)
qstr="("+k+"-"+m+")";
if(n==2)
qstr="("+k+"×"+m+")";
if(n==3)
qstr="("+k+"÷"+m+")";
}
return qstr;
}
private static String inffixToSuffix(String expression) {
stack.clear();
StringBuilder inffix = new StringBuilder(expression);
StringBuilder suffix = new StringBuilder();
String element = ""; // 中缀表达式的数字或者运算符
String tmp = "";
while (inffix.length() > 0) {
element = popNextElement(inffix);
if (isNum(element)) { // 是数字则输出
suffix.append(element).append(" ");
} else if (")".equals(element)) { // 右括号则将左括号之前的内容全弹出
tmp = stack.pop();
while (!"(".equals(tmp)) {
suffix.append(tmp).append(" ");
tmp = stack.pop();
}
} else if ("(".equals(element) || priority.get(element) >= priority.get(getTopOperator())) {
stack.push(element);
} else { // 优先级小于栈顶运算符,则弹出
tmp = stack.pop();
suffix.append(tmp).append(" ").append(element).append(" ");
}
}
// 把栈中剩余运算符都弹出
while (stack.size() > 0) {
suffix.append(stack.pop()).append(" ");
}
return suffix.toString();
}
/**
* 根据后缀表达式算出结果
* eg:中缀表达式8+(9-1)*8+7/2
* 后缀表达式8 9 1 - 8 * + 7 2 / +,元素之间之间用空格分隔。
* 从左到右遍历后缀表达式
* 遇到数字就进栈
* 遇到符号,就将栈顶的两个数字出栈运算,运算结果进栈,直到获得最终结果。
* -----------目前只支持整数,由于整数相除会出现浮点数,这里用String作为返回值--------
* @param expression 后缀表达式
* @return 结果
*/
private static String suffixToValue(String expression) {
// 已经用空格分隔,直接分割
String[] suffix = expression.split(" ");
stack.clear();
double num1 = 0, num2 = 0; // 注意次序,num2在栈顶,整数运算结果也可能是double
String tmp = "";
for (int i = 0; i < suffix.length; i++) {
if (isNum(suffix[i])) { // 数字
stack.push(suffix[i]);
} else{ // 是操作符
num2 = Double.parseDouble(stack.pop());
num1 = Double.parseDouble(stack.pop());
tmp = calculate(num1, num2, suffix[i]);
if (ERROR_ZERO.equals(tmp)) {
throw new ArithmeticException("被除数不能为0");
} else {
stack.push(tmp);
}
}
}
// 最终结果也压在栈中,取出即可
return stack.pop();
}
- 减少重复题目
程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
public QA_List(){
for(int a=0;a<i;a++)
{
//boolean x= new Random().nextBoolean();
Arithmetic hh = new Arithmetic(true);
int flag=1;
String int_str = hh.int_operation();
//String fra_str = hh.fra_operation();
if(true)
{
String str=hh.toString();
if(Qusetion.size()!=0){
for(int q=0;q<Qusetion.size();q++){
String qs=Qusetion.get(q);
int s;
for(s=0;s<hh.stack.size();s++){
String ss=hh.stack.get(s);
if(!qs.contains(ss)){
break;
}
}
if(s==hh.stack.size()){
flag=0;
break;
}
}
}
if(flag==0) a--;
else{
Answer.add(int_str);
Qusetion.add(str);
}
}
- 单元测试
回归测试
在开发新功能时避免损坏旧的功能,以确保新的功能不与原有功能冲突
在确认修改的功能正确之后再签入代码。
效能分析
效能分析工具:http://www.oschina.net/p/jprofiler ,使用方法: http://www.cnblogs.com/bjlhx/p/6668888.html
参考教材P29-34
参照“效能测试,分析,改进,再效能测试”的流程,找出关键模块消耗最大的函数,是否存在改进?
心得体会
结对编程的效率会比一个人来的高,首先我们会互相监督,其次两个脑袋在一起工作,脑力就是两倍啊。过程一个个问题呗解决还是很开心的。就是感觉任务有点重,因为上座的作业要阅读整本书提问题就耗去了不少用于结对编程的时间。所以几乎任务都是在这周完成的,非常的累。总体上结对编程还是很有意义的,两个脑袋的碰撞比一个人独自编程真的会开心不少
PSP
PSP2.1 | 个人开发流程 | 预估耗费时间(分钟) | 实际耗费时间(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 45 |
· Estimate | 明确需求和其他相关因素,估计每个阶段的时间成本 | 6 | 8 |
Development | 开发 | 320 | 400 |
· Analysis | 需求分析 (包括学习新技术) | 45 | 30 |
· Design Spec | 生成设计文档 | 15 | 15 |
· Design Review | 设计复审 | 30 | 45 |
· Coding Standard | 代码规范 | 5 | 8 |
· Design | 具体设计 | 120 | 180 |
· Coding | 具体编码 | 100 | 120 |
· Code Review | 代码复审 | 80 | 90 |
· Test | 测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 30 | 28 |
· | 测试报告 | 6 | 8 |
· | 计算工作量 | 5 | 8 |
· | 并提出过程改进计划 | 8 | 12 |