第一轮OO作业总结

vatican-cameos / 2024-04-22 / 原文

第一轮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类图

一开始我用数组来整合Textpaperanswerpapers,发现要多定义一个数组和多个计数器来确定试卷和答案所在的位置,十分鸡肋,故改用链表。

		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,分别判断题目是否被删除,题目是否本身就不存在。

还有一较大的改动是答案为空的问题,由于之前答案为顺序输出,答案为空的情况有且仅有可能出现在每题的末尾,我便偷懒在第二次作业中使nTextpaper中题目的数量,xAnswerpaper中已经有的题目数量,每次输入一个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.希望老师能在每次作业结束后,能公开题目的测试点,不然真的很难受。