结队项目
| 这个作业属于哪个课程 | 19级网三网四软件工程 |
|---|---|
| 这个作业要求在哪里 | 作业要求 |
| 这个作业的目标 | <实现一个自动生成小学四则运算题目的命令行程序,体会双人合作编程的感觉> |
项目成员:3119005346徐文俊,3119005337邱榕涛
一、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| Estimate | ·估计这个任务需要多少时间 | 5 | 5 |
| Development | 开发 | ||
| Analysis | ·需求分析 (包括学习新技术) | 180 | 280 |
| Design Spec | ·生成设计文档 | 40 | 40 |
| Design Review | ·设计复审 | 30 | 40 |
| Coding Standard | ·代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
| Design | ·具体设计 | 120 | 220 |
| Coding | ·具体编码 | 340 | 360 |
| Code Review | ·代码复审 | 30 | 30 |
| Test | ·测试(自我测试,修改代码,提交修改) | 60 | 70 |
| Reporting | 报告 | ||
| Test Repor | ·测试报告 | 30 | 25 |
| Size Measurement | ·计算工作量 | 20 | 15 |
| Postmortem & Process Improvement Plan | ·事后总结, 并提出过程改进计划 | 20 | 15 |
| ·合计 | 885 | 1110 |
二、效能分析
性能分析图:


三、设计实现过程。
本个项目主要包含了5个类:
Calculate类-负责计算
Check类-负责答案的对错判断
getExpression类-负责生成算术表达式、
Operation类-主要运行类
DuplicateCheck类-负责算数表达式的查重
还有两个实体类:分数类Fraction,算术表达式类OperationExpression
Fraction类的作用是进行分数之间的计算,里面写好了分数之间的加减乘除的计算方法,只要将分数传入类中,类就会将分子分母分开然后进行计算,结果会重新组合放出,如果是整数的话也是一样的,无非就是分母为1的分数。
生成一道算术表达式的流程-先实例化一个OperationExpression对象ex,再利用getExpression类的getExp方法生成一个表达式并且赋给ex,getExp()方法会调用了Calculate的calculate()计算方法得到之前生成的表达式的答案并将答案赋值给ex。如果calculate()方法中生成的两个数字经过运算后小于0则会返回空值,若存在负数则丢弃重新生成。然后,还会经过DuplicateCheck类对刚生成的算术式和之前生成的进行比对查重。通过上述步骤,即可生成一道题目。

四、部分代码说明。
分数类Fraction
package bean;
import java.util.Random;
//构建一个分数类,用来表示分数,封装相关的方法
public class Fraction {
private int nominator;// 分子
private int denominator;// 分母
// 构造方法
public Fraction(int nominator, int denominator) {
super();
this.nominator = nominator;
this.denominator = denominator;
}
public Fraction(int nominator) {
this.nominator = nominator;
this.denominator = 1;
}
public Fraction() {
super();
}
// 判断构建的是一个分数还是一个整数,不超过limit的数值
public Fraction(boolean l, int limit) {
Random r = new Random();
//分数
if (l == true) {
int index1 = r.nextInt(limit-1)+1;
int index2 = r.nextInt(limit-1)+1;//设定分子,分母其不会为零,取值范围为[1,limit)
System.out.println("分数"+index2 + " / " + index1);
//Random.nextInt(n) 生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
this.denominator = index1;
this.nominator = index2;
}
//整数
else {
int index = r.nextInt(limit-1)+1;//[1,limit)
this.denominator = 1;
this.nominator = index;
}
}
public int getDenominator() {
return denominator;
}
public void setDenominator(int denominator) {
this.denominator = denominator;
}
public int getNominator() {
return nominator;
}
public void setNominator(int nominator) {
this.nominator = nominator;
}
@Override
public String toString() {//返回值为字符串
Appointment();//约分
if(this.denominator == 0){//分母为零
System.out.println(this.nominator + "|" + this.denominator);
System.out.println("分母为0");
}
if (this.denominator == 1 || this.nominator == 0) {//分子为零
return "" + this.nominator;
}else if (this.nominator > this.denominator) {//分子大于分母
if(nominator % denominator==0){ //分子等于分母
return "" + nominator / denominator;
}
return "" + nominator / denominator + "'" + nominator % denominator + "/" + denominator;
}else{ //分子小于分母
return "" + this.nominator + "/" + this.denominator;//真分数
}
}
// 加法运算
public Fraction add(Fraction r) {
int a = r.getNominator();// 获得分子
int b = r.getDenominator();// 获得分母
int newNominator = nominator * b + denominator * a;
int newDenominator = denominator * b;
Fraction result = new Fraction(newNominator, newDenominator);
return result;
}
// 减法运算
public Fraction sub(Fraction r) {
int a = r.getNominator();// 获得分子
int b = r.getDenominator();// 获得分母
int newNominator = nominator * b - denominator * a;
int newDenominator = denominator * b;
Fraction result = new Fraction(newNominator, newDenominator);
return result;
}
// 分数的乘法运算
public Fraction muti(Fraction r) { // 乘法运算
int a = r.getNominator();// 获得分子
int b = r.getDenominator();// 获得分母
int newNominator = nominator * a;
int newDenominator = denominator * b;
Fraction result = new Fraction(newNominator, newDenominator);
return result;
}
// 分数除法运算
public Fraction div(Fraction r) {
int a = r.getNominator();// 获得分子
int b = r.getDenominator();// 获得分母
int newNominator = nominator * b;
int newDenominator = denominator * a;
Fraction result = new Fraction(newNominator, newDenominator);
return result;
}
// 用辗转相除法求最大公约数
private static long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
// 对分数进行约分
public void Appointment() {
if (nominator == 0 || denominator == 1)
return;
// 如果分子是0或分母是1就不用约分了
long gcd = gcd(nominator, denominator);
this.nominator /= gcd;
this.denominator /= gcd;
}
public int existZero(){
if(this.nominator<0||this.denominator<0){//如果分母或分子为负数
return 0;
}else {
return 1;
}
}
}
运行类Operation
public class Operation {
private static DuplicateCheck check = new DuplicateCheck();
private static getExpression get = new getExpression();
public static void main(String[] args) {
if(args.length==4){
if(args[0].equals("-n")){
int num = Integer.parseInt(args[1]);
int limit = Integer.parseInt(args[3]);
boolean result = mainGenerate(limit,num);
if(result){
System.out.println("题目文件生成在jar包同级文件夹");
}else {
System.out.println("程序出现错误");
}
}else {
String checked = args[1];
String answer = args[3];
boolean result = mainCheck(checked,answer);
if(result){
System.out.println("成绩文件生成在jar包同级文件夹");
}else {
System.out.println("程序出现错误");
}
}
}else {
System.out.println("请按规格输入参数 ");
System.out.println("说明参数1:-n [设置题目个数] -r [设置题目中数值的范围]");
System.out.println("数值范围指自然数、真分数和真分数分母");
System.out.println("说明参数2:-e [<exercisefile>.txt] -a [<answerfile>.txt]");
System.out.println("参数2为检查文件答案是否正确的指令");
System.out.println("请按规格输入参数");
}
}
public static boolean mainGenerate(int limit,int num){
BufferedOutputStream bo; // 写入到文件的缓冲流
BufferedOutputStream bo2;
List<List<String>> lists = new ArrayList<List<String>>();
Fraction res;
try {
File file = new File("./Exercises.txt");
File file2 = new File("./Answer.txt");
bo = new BufferedOutputStream(new FileOutputStream(file));
bo2 = new BufferedOutputStream(new FileOutputStream(file2));
// bo.write(date.getBytes());
// bo.write("\r\n".getBytes());
getExpression gExp = new getExpression();
DuplicateCheck check = new DuplicateCheck();
OperationExpression[] e = new OperationExpression[num];
List L = new ArrayList();
for (int i = 0; i<num; i++){
e[i] = new OperationExpression(null,null,null);
List l;
while ( (l = gExp.getExp(e[i],limit) )==null){
}
while(check.DuCheck(l,L)){
System.out.println("重复");
l = gExp.getExp(e[i],limit);//重复则重新生成表达式
}
System.out.println(l);
L.add(l);
bo.write((i+".").getBytes());
bo.write((String.join("",e[i].getStringList())+"=").getBytes());
bo.write("\r\n".getBytes());
bo2.write((i+".").getBytes());
bo2.write((String.join("",e[i].getStringList())+"="+e[i].getRes().toString()).getBytes());
bo2.write("\r\n".getBytes());
}
System.out.println();
for (Object i:L){
System.out.println(i);
}
bo.flush();
bo2.flush();
bo.close();
bo2.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
System.out.println("文件输入输出异常");
e.printStackTrace();
return false;
}
return true;
}
public static boolean mainCheck(String checked,String answer){
BufferedOutputStream bo = null; // 写入到文件的缓冲流
try {
File file = new File("./Grade.txt");
bo = new BufferedOutputStream(new FileOutputStream(file));
Map<String,String> m = Check.Check(checked, answer);
bo.write(m.get("wrong").getBytes());
bo.write("\r\n".getBytes());
bo.write(m.get("correct").getBytes());
bo.flush();
bo.close();
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
计算算术式答案类Calculate



获取算术表达式类getExpression


检查答案对错类Check

五、测试运行。
十个测试用例:

10000道算术表达式生成:




修改了些许答案后进行对错的比较:

六、项目小结。
这次项目是我们第一次的双人合作写程序,因为我们两人的java水平都不咋地,而且之前也从来没试过两个人合作写代码,所以这一次的项目编写过程颇为艰难。从一开始的讨论整体流程框架,定下主要的几个功能类,再到分工合作各自编写自己负责的类,其中因为水平问题也难免出现了不少的卡壳,上网查了很多资料,也参考了一下不少类似功能的代码,最后终于是勉强实现了功能。
从这次的合作中,我们最主要学到的就是沟通的重要性,如果两个人能沉下心来认真交换彼此的问题与意见,两个人能发挥出的作用是1+1>2的,从最初的确定分工、分析需求,到后面开始建立仓库进行实际开发,一步步最终项目成型。整个过程都需要及时沟通,这样不仅能提高效率,而且因为优势互补,达到"1+1>2"的效果。其次,我们也认识到了自己在编写项目这方面的经验的缺失,代码水平也不够,日后需要努力提升自身的代码能力。
最后通过这次结对项目,我们对软件工程这门课有了更深的理解,同时也体会到良好的沟通交流以及合理的分工是提高开发效率的利器。

浙公网安备 33010602011771号