第一轮OO作业总结
第一轮OO作业总结
题目梗概:设计实现答题程序,模拟一个小型的测试,要求输入题目信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。
前言
此次为我的第一次大作业,也是我第一次为迭代所苦恼的作业。三次作业主要考察我们对于类的认识,重点考察Java的三大基本特性之一,即“封装”,要求我们遵循类的设计原则:内聚性,一致性,封装性,清晰性等。题目包含众多错误判断,同时通过两次迭代,增加题目需求以要求同学们不断的修改代码以求完善,难度确实不容小觑。

设计与分析
第一次作业
- 思路 :将题目数量记录在
int n中,运用正则表达式拆分题目中的有用信息记录在自定义类Question中,在定义一个Questionpaper以集合的形式包含每个题目,即Question,而用户的回答则拆分进入自定义类Answerpaper中,将Questionpaper作为Answerpaper的一个参数,判断答案的真误,结果的拼接都在Answerpaper中完成。
UML类图

在第一次题目时,由于输入会直接给出题目数据,我便直接按行输入,用for循环决定输入的次数,所以输入的结束标志“end”对我来说就可有可无了,而在第一次作业中我就用了多个for循环与if语句的嵌套,为我的代码修改增加了难度,也为之后两次代码的复杂度的爆炸式增长做了铺垫。😦
for (int i = 0; i < n; i++) {
str2= input.nextLine(); //输入
Matcher matcher = pattern.matcher(str2);
if (matcher.matches()) {
a = Integer.parseInt(matcher.group(1).trim());
b = matcher.group(2).trim();
c = matcher.group(3).trim();
}
questionpaper.setNum(a);
questionpaper.setTitle(b);
questionpaper.setStanderAnswer(c);
questionpaper.setQuestion();
}
String result[]=new String[n];//用户输入的答案
for (int i = 0; i < n; i++) {
str= input.next();
Matcher matcher = pattern1.matcher(str);
if(matcher.matches()){
str=matcher.group(1).trim();//答案拆分
}
answerpaper =new Answerpaper(str,questionpaper,questionpaper.getQuestion(i+1).getTitle());
result[i]=answerpaper.getResult();
System.out.println(answerpaper.toString());
}
for (int i = 0; i < n; i++) {
if(i!=0)
System.out.printf(" ");
System.out.print(result[i]);
}
除此之外,当时设计时类的设定模糊不清,Questionpaper的作用似乎只是充当Question的链表。
度量
类
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| com.Answerpaper | 1.25 | 2 | 5 |
| com.Main | 7 | 7 | 7 |
| com.Question | 1 | 1 | 8 |
| com.Questionpaper | 1 | 1 | 10 |
方法
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| com.Answerpaper.Answerpaper() | 0 | 1 | 1 | 1 |
| com.Answerpaper.Answerpaper(String, Questionpaper, String) | 2 | 1 | 1 | 2 |
| com.Answerpaper.getResult() | 0 | 1 | 1 | 1 |
| com.Answerpaper.toString() | 0 | 1 | 1 | 1 |
| com.Main.main(String[]) | 9 | 1 | 7 | 7 |
第二次作业
增加了试卷类,信息可以混合输入,增加了判断分数等要求。
- 思路 :在输入之前将混乱输入的信息进行排序,将其计入在
stringNum中,顺序可定为先试题,后答卷,最后答案。在类方面,新增一个Textpaper类用来根据题号从Questionpaper中获取题目。
UML类图

一开始我用数组来整合Textpaper和answerpapers,发现要多定义一个数组和多个计数器来确定试卷和答案所在的位置,十分鸡肋,故改用链表。
int mm=0;
int aa=0;
Textpaper[] textpaper = new Textpaper [10];
int []numPaper = new int[10];
Answerpaper []answerpapers = new Answerpaper[10];
Questionpaper questionpaper=new Questionpaper();
LinkedList<Textpaper> textpaper = new LinkedList<>();
LinkedList<Answerpaper> answerpapers = new LinkedList<>();
可是我仍然用多个if语句判断输入的类型,还因为要进行排序多增加了几个循环,导致我main中的圈复杂度已经爆炸了。
度量
类
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| com.Answerpaper | 2 | 4 | 10 |
| com.Main | 18 | 18 | 18 |
| com.Question | 1 | 1 | 11 |
| com.Questionpaper | 1 | 1 | 3 |
| com.Textpaper | 1.22 | 3 | 11 |
方法
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| com.Answerpaper.Answerpaper() | 0 | 1 | 1 | 1 |
| com.Answerpaper.Answerpaper(Textpaper) | 0 | 1 | 1 | 1 |
| com.Answerpaper.Result() | 5 | 1 | 4 | 4 |
| com.Answerpaper.addanswer(String) | 0 | 1 | 1 | 1 |
| com.Answerpaper.toString() | 4 | 1 | 3 | 3 |
| com.Main.main(String[]) | 42 | 5 | 17 | 18 |
| com.Question.Question() | 0 | 1 | 1 | 1 |
| com.Question.Question(int, String, String) | 0 | 1 | 1 | 1 |
| com.Question.Question(int, String, String, int) | 0 | 1 | 1 | 1 |
| com.Question.getNum() | 0 | 1 | 1 | 1 |
| com.Question.getScore() | 0 | 1 | 1 | 1 |
| com.Question.getStanderAnswer() | 0 | 1 | 1 | 1 |
| com.Question.getTitle() | 0 | 1 | 1 | 1 |
| com.Question.setNum(int) | 0 | 1 | 1 | 1 |
| com.Question.setScore(int) | 0 | 1 | 1 | 1 |
| com.Question.setStanderAnswer(String) | 0 | 1 | 1 | 1 |
| com.Question.setTitle(String) | 0 | 1 | 1 | 1 |
| com.Questionpaper.Questionpaper() | 0 | 1 | 1 | 1 |
| com.Questionpaper.addQuestionLinkedList(int, String, String) | 0 | 1 | 1 | 1 |
| com.Questionpaper.getQuestionLinkedList() | 0 | 1 | 1 | 1 |
| com.Textpaper.Textpaper() | 0 | 1 | 1 | 1 |
| com.Textpaper.Textpaper(Questionpaper) | 0 | 1 | 1 | 1 |
| com.Textpaper.addtextquestion(int, int) | 3 | 3 | 3 | 3 |
| com.Textpaper.getNum() | 0 | 1 | 1 | 1 |
| com.Textpaper.getSumScore() | 0 | 1 | 1 | 1 |
| com.Textpaper.getTextLinkedList() | 0 | 1 | 1 | 1 |
| com.Textpaper.setNum(int) | 0 | 1 | 1 | 1 |
| com.Textpaper.setSumScore(int) | 0 | 1 | 1 | 1 |
| com.Textpaper.setTextLinkedList(LinkedList |
0 | 1 | 1 | 1 |
第三次作业
在上述问题的基础上,还要录入学生信息,并要实现删除题目信息的功能,对于信息的输入进行的丰富,增加了大量的错误判断。
思路:在上述问题的输入判断上多加几个判断条件,为了应对新的输入方式,修改正则表达式,并且增加Answer类,使Answerpaper与其形成聚合关系,新增Student类,记录学生的信息。
UML类图

此次对比第二次的作业的主要难点体现在输入的多样性,可输入空格,又可能输入错误的题数,要求我们对各种输入有应对的方法。其中又有两种输入错误,包括输入题号错误,以及题目已经删除却被引用,无奈,只能在Question中多定义两个布尔类型的变量isexist,existInQuestion,分别判断题目是否被删除,题目是否本身就不存在。
还有一较大的改动是答案为空的问题,由于之前答案为顺序输出,答案为空的情况有且仅有可能出现在每题的末尾,我便偷懒在第二次作业中使n为Textpaper中题目的数量,x为Answerpaper中已经有的题目数量,每次输入一个answer就使得x++,若最后x<n,则判定未输入的题目全为未回答的,并且在Answerpaper定义了一个void Result()来执行这个任务。
public Answerpaper(Textpaper textpaper) {
x=0;
this.textpaper = textpaper;
n=textpaper.getTextLinkedList().size();
}
public void addanswer(String answer){
answerLinkedList.add(answer);
x++;
}
public void Result(){
while(x<n){
answerLinkedList.add("null");
x++;
}
for (int j = 0; j < n; j++) {
if(answerLinkedList.get(j).equals(textpaper.getTextLinkedList().get(j).getStanderAnswer())){
result.add("true");
str2=str2+textpaper.getTextLinkedList().get(j).getScore()+" ";
sumScore+=textpaper.getTextLinkedList().get(j).getScore();
}
else {
str2 = str2 + 0 + " ";
result.add("false");
}
}
}
如此操作使我在第三次作业时傻了眼,只好在每次添加answer时判断答题卷是否满了。
if(!answerpapers.getLast().isfull())
answerpapers.getLast().setanswer(id,answer);
诸多的判断使我不断添加if进行判断,终于仅仅Main中的圈复杂度达到了惊人的86,本质复杂度也达到了10。
度量
类
| Class | OCavg | OCmax | WMC |
|---|---|---|---|
| com.Answer | 1 | 1 | 7 |
| com.Answerpaper | 2.33 | 7 | 14 |
| com.Main | 32 | 32 | 32 |
| com.Question | 1 | 1 | 15 |
| com.Questionpaper | 1 | 1 | 3 |
| com.Student | 1 | 1 | 6 |
| com.Textpaper | 1.44 | 5 | 13 |
方法
| Method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| com.Answer.Answer() | 0 | 1 | 1 | 1 |
| com.Answer.Answer(String) | 0 | 1 | 1 | 1 |
| com.Answer.Answer(String, int) | 0 | 1 | 1 | 1 |
| com.Answer.getAnswer() | 0 | 1 | 1 | 1 |
| com.Answer.getId() | 0 | 1 | 1 | 1 |
| com.Answer.setAnswer(String) | 0 | 1 | 1 | 1 |
| com.Answer.setId(int) | 0 | 1 | 1 | 1 |
| com.Answerpaper.Answerpaper() | 0 | 1 | 1 | 1 |
| com.Answerpaper.Answerpaper(Textpaper, Student) | 1 | 1 | 2 | 2 |
| com.Answerpaper.addanswer(int, String) | 0 | 1 | 1 | 1 |
| com.Answerpaper.isfull() | 1 | 2 | 1 | 2 |
| com.Answerpaper.setanswer(int, String) | 0 | 1 | 1 | 1 |
| com.Answerpaper.toString() | 9 | 2 | 7 | 7 |
| com.Main.main(String[]) | 86 | 10 | 31 | 33 |
| com.Question.Question() | 0 | 1 | 1 | 1 |
| com.Question.Question(int, String, String) | 0 | 1 | 1 | 1 |
| com.Question.Question(int, String, String, int) | 0 | 1 | 1 | 1 |
| com.Question.getNum() | 0 | 1 | 1 | 1 |
| com.Question.getScore() | 0 | 1 | 1 | 1 |
| com.Question.getStanderAnswer() | 0 | 1 | 1 | 1 |
| com.Question.getTitle() | 0 | 1 | 1 | 1 |
| com.Question.isExistInQuestion() | 0 | 1 | 1 | 1 |
| com.Question.isIsexist() | 0 | 1 | 1 | 1 |
| com.Question.setExistInQuestion(boolean) | 0 | 1 | 1 | 1 |
| com.Question.setIsexist(boolean) | 0 | 1 | 1 | 1 |
| com.Question.setNum(int) | 0 | 1 | 1 | 1 |
| com.Question.setScore(int) | 0 | 1 | 1 | 1 |
| com.Question.setStanderAnswer(String) | 0 | 1 | 1 | 1 |
| com.Question.setTitle(String) | 0 | 1 | 1 | 1 |
| com.Questionpaper.Questionpaper() | 0 | 1 | 1 | 1 |
| com.Questionpaper.addQuestionLinkedList(int, String, String) | 0 | 1 | 1 | 1 |
| com.Questionpaper.getQuestionLinkedList() | 0 | 1 | 1 | 1 |
| com.Student.Student() | 0 | 1 | 1 | 1 |
| com.Student.Student(String, String) | 0 | 1 | 1 | 1 |
| com.Student.getId() | 0 | 1 | 1 | 1 |
| com.Student.getName() | 0 | 1 | 1 | 1 |
| com.Student.setId(String) | 0 | 1 | 1 | 1 |
| com.Student.setName(String) | 0 | 1 | 1 | 1 |
| com.Textpaper.Textpaper() | 0 | 1 | 1 | 1 |
| com.Textpaper.Textpaper(Questionpaper) | 0 | 1 | 1 | 1 |
| com.Textpaper.addtextquestion(int, int) | 5 | 3 | 4 | 5 |
| com.Textpaper.getNum() | 0 | 1 | 1 | 1 |
| com.Textpaper.getSumScore() | 0 | 1 | 1 | 1 |
| com.Textpaper.getTextLinkedList() | 0 | 1 | 1 | 1 |
| com.Textpaper.setNum(int) | 0 | 1 | 1 | 1 |
| com.Textpaper.setSumScore(int) | 0 | 1 | 1 | 1 |
| com.Textpaper.setTextLinkedList(LinkedList |
0 | 1 | 1 | 1 |
踩坑心得
不要在基于标记的输入之后使用基于行的输入
参考链接
在第一次作业时我就犯了这个错误,在输入题目数量时用·了nextInt(),而后输入的换行直接被nextLine()吃了,导致第一次输入为空,为了解决这个问题,在题目数量输入和题目的输入之间用nextLine()清楚换行。
int n= input.nextInt(); //题目数目
Questionpaper questionpaper = new Questionpaper(n);
Answerpaper answerpaper; //试卷
String str,str2;
int a=0; //题号
String b=""; //问题
String c=""; //标准答案
input.nextLine();
for (int i = 0; i < n; i++) {
str2= input.nextLine(); //输入
Matcher matcher = pattern.matcher(str2);
if (matcher.matches()) {
a = Integer.parseInt(matcher.group(1).trim());
b = matcher.group(2).trim();
c = matcher.group(3).trim();
}
questionpaper.setNum(a);
questionpaper.setTitle(b);
questionpaper.setStanderAnswer(c);
questionpaper.setQuestion();
}
改进建议
1.输入存储时不用Linkedlist改用HashMap,将键设定为#N等,减少if的使用
2.将正则表达式的拆分包装成一个函数,增加代码的复用性,简洁性。
总结
学习总结
1.学习了类的概念与具体使用,通过三次的作业,我已经基本学会在Java中使用类。与此同时,也发现自己编写的类过度耦合,无法做到真正的独立。
2.学习了正则表达式,三次作业的输入的核心考点就为正则表达式的考察。通过特别是第三次作业,我发现自己正则表达式的Pattern对象仍然有不足。
建议
1.课堂的讲课进度对于作业来说有点过慢了,如第三次作业中要用到和Comparable接口与抽象类的第13章知识,但当我写下这篇blog时,课程才讲到第11章。
2.题目样例太片面了,如第二次作业的一个点-“答案为空字符,按正常输入处理”描述的模模糊糊,什么叫“答案为空也可以”,为空应该输出什么却不说,全靠试;还有第三次作业,那个乱序输入真的是我的一生之敌,后来才知道,和我的乱不乱一点关系也没有,是输入可能不单单是数学题,而第二次作业如果全考虑数学题就能通过,而关于这方面的描述仅仅出现在第一次作业的题干中。
3.希望老师能在每次作业结束后,能公开题目的测试点,不然真的很难受。