oop1-3次作业总结

huaibo-111 / 2024-04-21 / 原文

一、前言

本来以为面向对象程序设计会和上个学期的c语言一样的简单,可以轻松地通过测试,但是经过了这三次的作业我明白了这是一门需要我去认真对待的课程,不能马马虎虎,好不容易做完了,最后听了老师的课才发现自己的类设计出现了问题,下次作业一定要把这些基础的问题解决。

这三次的作业都是围绕着学生的答题判题逐步加深复杂,让我们一遍遍的修改,前两次的测试点并不***难还是能找到错误并通过的,但是第三次的测试就出现了解决不了的问题,一直找不到问题出在哪里,其实主要的问题还是出现在类的设计上,这个才是我真正要慎重考虑的,不能盲目的code。

这几次作业都需要我们搞清楚类与类之间的关系,熟练地使用正则表达式判断输入字符串的格式,但其中的错误判断也总是让人防不胜防,同时也考验了我们的程序调试能力,那么下面就来详细地讲讲这三次作业吧。

二、设计与分析

1、答题判题程序-1
设计实现答题程序,模拟一个小型的测试,要求输入题目信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。
输入格式:
程序输入信息分三部分:

1、题目数量

格式:整数数值,若超过1位最高位不能为0,

样例:34

2、题目内容

一行为一道题,可以输入多行数据。

格式:"#N:"+题号+" "+"#Q:"+题目内容+" "#A:"+标准答案

格式约束:题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。

样例:#N:1 #Q:1+1= #A:2

     #N:2 #Q:2+2= #A:4

3、答题信息

答题信息按行输入,每一行为一组答案,每组答案包含第2部分所有题目的解题答案,答案的顺序号与题目题号相对应。

格式:"#A:"+答案内容

格式约束:答案数量与第2部分题目的数量相同,答案之间以英文空格分隔。

这次的作业是我第一次将课上听到的内容应用到作业中去,首先要考虑的就是类的设计,但是题目贴心地把设计建议给了我们,所以我就直接分成了题目类、试卷类和答卷类,类的设计可以参考下面PowerDesigner生成的类图。

然后就是正则表达式,一开始想的是把#号后面的字符串都识别出来并存入字符串变量中,再进行删除多余字符的操作,代码如下。

        Scanner input=new Scanner(System.in);
        int amount=input.nextInt();
        String c=input.nextLine();
	String regStr="(#.*)(#.*)(#.*)";
	Paper paper=new Paper(amount);
        for(int i=1;i<=amount;i++){
		String s=input.nextLine();
		Pattern pattern=Pattern.compile(regStr);
		Matcher matcher=pattern.matcher(s);
		while(matcher.find()) {
                    String tN=matcher.group(1);
                    String tQ=matcher.group(2);
                    String tSA=matcher.group(3);
                    String tN1=tN.replaceAll("\\#N\\:\\s*","");
                    String N=tN1.replaceAll("\\s*","");
                    String Q=tQ.replaceAll("\\#Q\\:\\s*","");
                    Q=Q.replaceAll("\\s+$","");
                    String SA=tSA.replaceAll("\\#A\\:\\s*","");
                    int num = Integer.parseInt(N);
                    Question question=new Question();
                    question.setNo(N);
                    question.setContent(Q);
                    question.setStandardAnswer(SA);
                    paper.setQuestion(num, question);
		}
        }

然而这样的办法显然麻烦了,应该直接通过分组提取题目内容省去删除无关内容的这一步。

总的来说,第一次的作业还是考虑到我们刚刚学习java,难度上还是没有为难我们的。

2、答题判题程序-2
设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-1基础上增补或者修改的内容。
要求输入题目信息、试卷信息和答题信息,根据输入题目信息中的标准答案判断答题的结果。

第二次作业加入了试卷信息,两种信息顺序输入变为了三中信息的乱序输入了,同时还要计算总分,首先我添加了一个答题类来聚合答卷类和试卷类以便于对应答卷和试卷,从而判题和计分,PowerDesigner生成的类图如下

考虑到需要关联序号和试卷,所以使用HashMap的类型创建了questionlist来存放题目类的对象。

第二题多了一种正则表达式的判别,试卷信息的判别,同时答卷信息也同样加入了序号,以下是二者的正则表达式。

String regStr2="#T:(\\d+)(?:\\s+(\\d+)-(\\d+))+";
String regStr3="#S:(\\d+)\\s+(?:#A:(.+))+";

与上一题不同的是这道题加入了大量的格式要求,下面就来详情介绍这些格式要求如何满足。

  • 试卷总分警示
    在试卷类有方法fullscore()计算总分,若总分不为100分则输出"alert: full score of test paper"+试卷号+" is not 100 points"在输出之前就需要最先判断这个。
public int fullscore() {
		int sum=0;
		for(int i=1;i<=Amount;i++) {
			sum+=Score[i];
		}
		return sum;
	}
for(int i=1;i<=paperamount;i++) {
			if(paperlist.get(Integer.parseInt(paperNo[i])).fullscore()!=100) {
				System.out.println("alert: full score of test paper"+paperNo[i]+" is not 100 points");
			}
		}
  • 提示错误的试卷号
    有时候答案信息的试卷编号并不存在,这时候就要判断是否有与答案中试卷编号匹配的试卷,可以判断答案中编号的试卷在试卷类的数组paperlist中是否能找到。

paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo()))

若这段代码中i是由1到n(答卷的数量)的,判断对应的试卷是否存在,若为null则输出”the test paper number does not exist”。

  • 答案缺失提示
    如果输入的答案信息少于试卷的题目数量,答案的题目要输"answer is null",那么我可以计算输入的答案数和试卷的题目数,若输入的答案数少于试卷的题目数则二者相减就是要输出的"answer is null"的个数。
	public int getsubtract() {
		return paper.getAmount()-answerpaper.getAmount();
	}

这是在答案类中的方法,可以得到二者题目数的差值。

for(int j=1;j<=answer_.getsubtract();j++){
                        System.out.println("answer is null");
                    }

然后通过for循环输出。

3、答题判题程序-3
设计实现答题程序,模拟一个小型的测试,以下粗体字显示的是在答题判题程序-2基础上增补或者修改的内容,要求输入题目信息、试卷信息、答题信息、学生信息、删除题目信息,根据输入题目信息中的标准答案判断答题的结果。

第三次作业相对于第二次作业多了学生信息和删除题目信息,同时在输出答案时不止有试卷编号,同时还要有学生的姓名和学号,在输出成绩时也要对应相应的学生,可以说这次的作业也只是在上次的基础上添加了一些基础信息,但是这次作业的坑远远比上次的坑要多,是真的要反复地去测试才能有头绪去修改代码通过测试,但是我到了最后还是有几个点不知道如何去修改,下面先来谈谈类的设计。

因为这次多了学生信息和删除题目信息,所以我添加了学生类和删除题目类,但写到现在这样来设计类的关系明显是不合理的,并没有满足单一职责原则,所以写到后面这个类的结构越来越乱了,出现错误也很难找到在哪里,可以说是一次深刻的教训了。

首先就是正则表达式的改动,有两处添加和一处改变,其中两处添加的是

String regStr4="#X:\\d+\\s+\\D+.*";
String regStr5="#D:N-(\\d+)";

这其中regStr4在捕获成功后还要改变捕获模式重新捕获信息

if(matcher4.find()) {
		    	flag=1;
		    	s = s.substring(3);
		    	Pattern pattern4_ = Pattern.compile("(\\d+)\\s+([^\\d-]+)");
		    	Matcher matcher4_ = pattern4_.matcher(s);
		    	while (matcher4_.find()) {  
		            String studentNo = matcher4_.group(1);
		            String name = matcher4_.group(2);
		            Student student=new Student(studentNo,name);
		            studentlist.put(studentNo, student);
		        }  
		    }

先用s = s.substring(3);裁剪掉字符串的前三个字符,然后再将字符串中重要的数字捕获。

那一个修改是答案信息的修改,由于答案信息再输入试卷编号后还要输入答卷人的学号和姓名所以更改的新的正则表达式来捕获

String regStr3="#S:(\\d+)\\s+(\\d+)\\s*(?:#A:(.+))*";

同样的regStr3在捕获成功后还要改变捕获模式重新捕获信息

if(matcher3.find()) {
		    	flag=1;
		    	answerpaperamount++;
                String[] answer=new String[101];
                int[] answernum=new int[101];
		    	answerpaperNo[answerpaperamount]=matcher3.group(1);
		    	String StudentNo=matcher3.group(2);
                Pattern pattern3_ = Pattern.compile("\\s+#A:(\\d+)-(\\d*)");  
                Matcher matcher3_ = pattern3_.matcher(s);
                while (matcher3_.find()){
		    		amount2++;
		    		answernum[amount2]=Integer.parseInt(matcher3_.group(1));
		    		answer[amount2]=matcher3_.group(2);
                }
		    	AnswerPaper answerpaper=new AnswerPaper(answerpaperNo[answerpaperamount],answer,amount2,StudentNo,answernum);
		    	answerpaperlist.add(answerpaper);
		    }

然后就是第三题新添加的输出要求:

  • 被删除的题目提示信息
    试卷引用被删除了的题目就要输出the question + 试卷号 + invalid~0。
    所以我把题目内容改为了the question + 试卷号 + invalid,题目的答案改为null在输出答案前先判断题目是否被删除。
class DeletQuestion{
	HashMap<Integer, Question> questionlist = new HashMap<Integer, Question>();
	
	public DeletQuestion() {
		super();
	}

	public DeletQuestion(HashMap<Integer, Question> questionlist) {
		super();
		this.questionlist = questionlist;
	}

	public HashMap<Integer, Question> deletQuestion(HashMap<Integer, Question> questionlist , String No) {
		questionlist.get(Integer.parseInt(No)).setContent("the question "+No+" invalid");
		questionlist.get(Integer.parseInt(No)).setStandardAnswer("null");
		return questionlist;
	}
}

通过deletQuestion方法来改变对应题目的信息。

  • 题目引用错误提示信息
    试卷错误地引用了一道不存在题号的试题,在输出学生答案时,提示”non-existent question~”加答案。
    所以判断在试卷类的对象中这道题是否存在
    if(paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo())).getquestion(j-1)==null)
    若判断为不存在则提示”non-existent question~”加答案。

  • 格式错误提示信息

加入一个初值为0的flag变量,若匹配到了格式正确的字符串就让flag=1,最后判断,若flag为0则输出”wrong format:”+信息内容。

if(flag==0)
		    	System.out.println("wrong format:"+s);
  • 学号引用错误提示信息
    在答卷学生信息不存在时要输出 学号+" not found",只要加上一个判断if(studentlist.get(answerpaperlist.get(i-1).getStudentNo())==null)若为真则输出对应提示信息。

三、踩坑心得

第一题
第一题由于给了我们类的设计建议所以并没用太大的问题,做起来还是比较顺利。

第二题
之前并没有判断是否有与答案中试卷编号匹配的试卷就直接调用试卷类中的方法,但由于根据答案中的编号找不到对应试卷,所以程序报错。

if(paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo())).getAmount()<=answerpaperlist.get(i-1).getAmount())

这里的paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo())).getAmount()找到的是null,所以应该在前面就判断是否找到了试卷类的对象再使用试卷类的方法。

后来在使用试卷类的方法前加入了判断

if(paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo()))!=null)

这个问题也就迎刃而解了。

第三题

  1. 正则问题
    一开始并没有想到这个答案信息可以没有答案,开始的时候正则表达式是

String regStr3="#S:(\\d+)\\s+(\\d+)\\s*(?:#A:(.+))+";

但是测试时发现#S:1 20201103这样的信息会使得程序输出wrong format:#S:1 20201103,这显然是不对的,后来把正则表达式改为了

String regStr3="#S:(\\d+)\\s+(\\d+)\\s*(?:#A:(.+))*";

程序就能够正常判别了。

2.答案为空字符时
此时输出到这行信息时,程序返回的是"answer is null",但题目要求我们正常输出,后来发现到是在正则表达式匹配时就默认了答案的位置一定有数字

Pattern pattern3_ = Pattern.compile("\\s+#A:(\\d+)-(\\d+)");

所以更改为

Pattern pattern3_ = Pattern.compile("\\s+#A:(\\d+)-(\\d*)");

3.计分出现试卷答案数比答卷答案数少时计分信息会少几个零
由于自身的疏忽,计分时是按照答卷的题目数来输出就导致"0 00"变为了"00"应该把
for(int j=2;j<=answerpaperlist.get(i-1).getAmount();j++)
改为
for(int j=2;j<=paperlist.get(Integer.parseInt(answerpaperlist.get(i-1).getNo())).getAmount();j++)

四、改进建议

这几次的作业最大的不足就是类的关系混乱,我想下次必须要重写类的关系设计,其次就是代码的可阅读性不强,在写代码时还没有写注释的习惯,这让我很难发现自己的错误,所以下次一定会改进这些最基础的问题,同样的下次的任务是为代码改为继承结构,这些都是可以改进的地方。

五、总结

在这几次的作业中我学到了如何使用正则表达式,同时也学到了注重格式的重要性,有时候看着那挤在一起的代码真的不知道该何从下手,从这几次难度略大的题目的做题记录我明白了一定不能去逃避,越是不想做就越是做不成,所以平时的作业一定要认真对待,不该一直想着去拖了。

我觉得自己类与类之间的关系还是理解得不够透彻,应该多花时间好好想想该如何去设计一个项目,站在面向对象的角度去思考。