第一阶段三次PTA作业总结
一、前言
这三次的PTA作业除去最后一题都是 “答题判题程序” 外,每道题目的难度较为简单,题量相对较小,主要知识点涉及如何使用类与对象、基本语法、类与类之间的关系、类的设计等,而最主要的三道 “答题判题程序” 难度相对较大,涉及知识较为繁杂,其中最主要的是如何设计好类及其结构,而好的类的结构非常重要,因为它直接影响到程序的可读性、可维护性和可扩展性。
二、设计与分析
接下来,我将会对三次答题判题程序的代码进行分析。
1、答题判题程序-1
点击查看代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.nextLine();
Textpaper textpaper = new Textpaper(n);
for(int i = 0; i < n; i++) {
String inputs = scanner.nextLine();
String[] input = inputs.split("#N:| #Q:| #A:");
String num = input[1].trim();
String content = input[2].trim();
String answer = input[3].trim();
Subject subject = new Subject();
subject.setNum(num);
subject.setContent(content);
subject.setAnswer(answer);
textpaper.setSubject(num, subject);
}
String[] input1 = scanner.nextLine().split(" ");
Answersheet answersheet = new Answersheet(n);
for(int i = 0; i < n; i++) {
String answer1 = input1[i].replace("#A:","");
answersheet.setAnswer(i, answer1);
}
for(int i = 0; i < n; i++) {
Subject subject = textpaper.getSubjectByIndex(i);
if(subject != null) {
String content = subject.getContent().trim();
System.out.println(content + "~" + answersheet.getAnswer()[i]);
}
}
for(int i = 0; i < n; i++) {
Subject subject = textpaper.getSubjectByIndex(i);
if(subject != null) {
if(subject.getAnswer().equals(answersheet.getAnswer()[i])) {
System.out.print("true");
} else {
System.out.print("false");
}
if(i != n - 1) {
System.out.printf(" ");
}
}
}
}
}
class Subject {
private String num;
private String content;
private String answer;
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public boolean judgeAnswer(String answer) {
return this.answer.equals(answer);
}
}
class Textpaper {
private int n;
private HashMap<String, Subject> subjectMap;
public Textpaper(int n) {
this.n = n;
this.subjectMap = new HashMap<>();
}
public void setSubject(String num, Subject subject) {
this.subjectMap.put(num, subject);
}
public Subject getSubjectByIndex(int index) {
if(index >= 0 && index < n) {
return subjectMap.get(String.valueOf(index + 1));
}
return null;
}
}
class Answersheet {
private int n;
private String[] answer;
public Answersheet(int n) {
this.n = n;
this.answer = new String[n];
}
public void setAnswer(int i, String answer) {
this.answer[i] = answer;
}
public String[] getAnswer() {
return answer;
}
}
以下是对代码的复杂度分析


以下是代码的类结构图

当初写这个代码的时候,并没有考虑到代码的可读性、可维护性和可扩展性等等,而是抱着一种完成任务的心态,所以这次的类设计是有很大问题的。
通过上面代码的复杂度分析可以看到,虽然代码总体的Avg Complexity为1.79,大概是因为其他的三个类设计较为简单,拉低了平均复杂度,但是可以看到Main类的复杂度为10,说明Main类代码的复杂度是很高的。(当然,我把输入字符串的处理,各种判断以及输出数据全部混合在一起,复杂度不高才怪QAQ)
通过类结构图可以直观的看到,除Main类外,一共设计了三个类,Subject类用来储存单个题目的信息,Textpaper则是和Subject类之间有关联关系,Textpaper类中储存了Subject题目类,而Answersheet类则是单独的一个类,它主要是用来储存输入的答案,输出时将Answersheet类中的答案与Textpaper类中的题目答案进行判断,最后输出对应数据。(通过类结构图就可以发现,我并没有设计专门的类来处理字符串、进行试卷题目答案的判断、输出对应数据等等,而是全部混杂在Main类中,所以这个代码的可维护性,可扩展性基本为零QAQ,也违背了单一职责原则。关于这一点我在写代码时并没有很清楚的认知)
这里说明一点,当然写代码之前我通过课程了解到单一职责原则的重要性,个人认为自己已经理解了这一神圣的原则,到现在来看,自己是如此目光短浅qmq。确实当时我认为的单一职责原则就是把Subject、Answersheet、Textpaper这几个类拆开分工使用,而我也仅仅是只考虑了这一点,结果就是Main类中的大乱炖(╯▔皿▔)╯。
代码的输入部分还是学习了使用正则表达式来处理字符串,之前也尝试过使用if判断语句来处理字符串,结果就是令人眼花缭乱的一坨答辩,有一说一,正则表达式确实很是方便。
如何将题号与题目进行绑定,我是在Subject题目类中设置了num题号属性来判断,而如何将题目写入试卷Textpaper中,则是使用了HashMap集合类,当时为了想出将题目写入试卷的方法时可谓是绞尽脑汁,最终还是在ChatGPT的帮助下了解了HashMap这一集合类才得以解决(●'◡'●)。
总体来说,答题判题程序-1的难度并不是很大,像我这种一塌糊涂的类结构设计都能得到满分/(ㄒoㄒ)/~~
2、答题判题程序-2
点击查看代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
LinkedHashMap<String, Subject> subjectMap = new LinkedHashMap<>();
HashMap<String, Textpaper> textpaperMap = new HashMap<>();
HashMap<String, Answer> answerMap = new HashMap<>();
String line;
while (!(line = scanner.nextLine()).equals("end")) {
if (line.startsWith("#N:")) {
String[] input = line.split("#N:| #Q:| #A:");
String num = input[1].trim();
String content = input[2].trim();
String answer = input[3].trim();
Subject subject = new Subject();
subject.setNum(num);
subject.setContent(content);
subject.setAnswer(answer);
subjectMap.put(num, subject);
}
else if (line.startsWith("#T:")) {
String[] input = line.split("#T:| ");
String testPaperNum = input[1];
String[] items = Arrays.copyOfRange(input, 2, input.length);
Textpaper textpaper = new Textpaper();
for (String item : items) {
String[] item1 = item.split("-");
String num = item1[0];
int score = Integer.parseInt(item1[1]);
textpaper.setSubject(num, subjectMap.get(num), score);
}
textpaperMap.put(testPaperNum, textpaper);
}
else if (line.startsWith("#S:")) {
String[] input = line.split("#S:| #A:");
String testPaperNum = input[1];
String[] answers = Arrays.copyOfRange(input, 2, input.length);
if (answerMap.containsKey(testPaperNum)) {
answerMap.get(testPaperNum).addAnswer(answers);
}
else {
Answer answer = new Answer();
answer.addAnswer(answers);
answerMap.put(testPaperNum, answer);
}
}
}
for (String testPaperNum : textpaperMap.keySet()) {
Textpaper textpaper = textpaperMap.get(testPaperNum);
if (textpaper.getTotalScore() != 100) {
System.out.println("alert: full score of test paper" + testPaperNum + " is not 100 points");
}
}
for (String testPaperNum : textpaperMap.keySet()) {
Textpaper textpaper = textpaperMap.get(testPaperNum);
Answer answer = answerMap.get(testPaperNum);
if (answer == null) {
System.out.printf("The test paper number does not exist");
System.exit(0);
}
for (String[] answers : answer.getAnswersList()) {
int j = 0;
for (String subjectNum : textpaper.getSubjectMap().keySet()) {
Subject subject = textpaper.getSubjectByIndex(subjectNum);
if (subject != null && j < answers.length) {
System.out.print(subject.getContent() + "~" + answers[j] + "~");
if (subject.judgeAnswer(answers[j])) {
System.out.println("true");
subject.setFlag(1);
}
else {
System.out.println("false");
subject.setFlag(0);
}
}
else {
System.out.printf("answer is null\n");
}
j ++;
}
int totalScore = 0;
int i = 0;
for (String subjectNum : textpaper.getSubjectMap().keySet()) {
Subject subject = textpaper.getSubjectByIndex(subjectNum);
if (i < answers.length) {
if (subject.judgeAnswer(answers[i])) {
totalScore += textpaper.getSubjectScore(subjectNum);
}
}
if ((i != textpaper.getSubjectCount() - 1)) {
if (subject.getFlag() == 1) {
if (i < answers.length)
System.out.print(textpaper.getSubjectScore(subjectNum) + " ");
else
System.out.print("0" + " ");
}
else
System.out.printf("0" + " ");
}
else {
if (subject.getFlag() == 1) {
if (i < answers.length)
System.out.print(textpaper.getSubjectScore(subjectNum));
else
System.out.print("0");
}
else
System.out.printf("0");
}
i ++;
subject.setFlag(0);
}
System.out.println("~" + totalScore);
}
}
}
}
class Subject {
private String num;
private String content;
private String answer;
private int flag = 0;
public void setFlag(int num) {
this.flag = num;
}
public int getFlag() {
return flag;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public boolean judgeAnswer(String answer) {
return this.answer.equals(answer);
}
}
class Textpaper {
private LinkedHashMap<String, Subject> subjectMap;
private LinkedHashMap<String, Integer> subjectScore;
public Textpaper() {
this.subjectMap = new LinkedHashMap<>();
this.subjectScore = new LinkedHashMap<>();
}
public void setSubject(String num, Subject subject, int score) {
this.subjectMap.put(num, subject);
this.subjectScore.put(num, score);
}
public void setSubjectScore(String num) {
this.subjectScore.replace(num, 0);
}
public HashMap<String, Subject> getSubjectMap() {
return subjectMap;
}
public Subject getSubjectByIndex(String num) {
return subjectMap.get(num);
}
public int getSubjectScore(String num) {
return subjectScore.get(num);
}
public int getSubjectCount() {
return subjectMap.size();
}
public int getTotalScore() {
int totalScore = 0;
for (int score : subjectScore.values()) {
totalScore += score;
}
return totalScore;
}
}
class Answer {
private List<String[]> answersList;
public Answer() {
this.setAnswersList(new ArrayList<>());
}
public void addAnswer(String[] answers) {
this.getAnswersList().add(answers);
}
public List<String[]> getAnswersList() {
return answersList;
}
public void setAnswersList(List<String[]> answersList) {
this.answersList = answersList;
}
}
以下是对代码的复杂度分析


以下是代码的类结构图

答题判题程序-2 在 答题判题程序-1的基础上增加了乱序输入、可输入多张试卷、答案数量可以不等于试卷信息中题目的数量、试卷总分警示以及题目得分。
通过代码的复杂度分析图可以看到,代码的复杂度更高了QAQ,这次代码实在上次的基础上进行修改得到的,因为上次的代码可扩展性和可维护性几乎为零,所以这次代码修改起来格外的困难,而得到的代码也是垃圾代码。可以看到,Main类的Avg Complexity达到了惊人的32!!天哪!在写第二次作业的时候我还是没能正确认识到正确类结构设计的重要性,还是在傻乎乎的修改、增加功能。
再看类结构图,第二次相比第一次的类结构几乎没有变化,只是在相应的类中增加了一些方法(这也成功导致了第三次作业修改的可能性几乎变为了不可能)。
关于增加的功能(除去垃圾的类结构外),乱序输入则是采用了if、else if、else条件判断语句来实现,通过使用startsWith()判断字符串开头来进行相应的字符串处理。而对于多张试卷和答卷的处理,就是简简单单的在Main类中使用了
HashMap<String, Textpaper> textpaperMap = new HashMap<>();
HashMap<String, Answer> answerMap = new HashMap<>();
在Textpaper试卷类中我将private HashMap<String, Subject> subjectMap;改为了private LinkedHashMap<String, Subject> subjectMap;。
每道题目的分数同样使用了private LinkedHashMap<String, Integer> subjectScore;
因为HashMap 不能保证键值对的顺序,它会根据键的哈希值进行排列,在我的代码中会出现题目顺序出现混乱,而LinkedHashMap 可以保证键值对的顺序,它可以按照插入顺序或者访问顺序来排列键值对,所以我将HashMap改为了LinkedHashMap。
剩下的功能增加时较为简单,只需用if条件判断语句即可完成功能。
第二次作业我其实并没有得到满分,还有一点缺陷差两个测试点,尽管我不断地调试查找,始终无法找出问题所在(已经尽力了QAQ)。
总的来说,第二次作业的代码和第一次一样,都是一坨答辩、垃圾代码!直到此时我还是依然沉浸在得分的喜悦当中,并没有认识到自己的代码存在着巨大缺陷,而这个缺陷将会在第三次作业给予我沉重的打击/(ㄒoㄒ)/~~。
3、答题判题程序-3
点击查看代码
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
LinkedHashMap<String, Subject> subjectMap = new LinkedHashMap<>();
HashMap<String, Textpaper> textpaperMap = new HashMap<>();
HashMap<String, Answer> answerMap = new HashMap<>();
HashMap<String, Information> informationMap = new HashMap<>();
HashMap<String, Information> studentMap = new HashMap<>();
String line;
while (!(line = scanner.nextLine()).equals("end")) {
if (line.startsWith("#N:")) {
String[] input = line.split("#N:| #Q:| #A:");
String num = input[1].trim();
String content = input[2].trim();
String answer = input[3].trim();
Subject subject = new Subject();
subject.setNum(num);
subject.setContent(content);
subject.setAnswer(answer);
subjectMap.put(num, subject);
}
else if (line.startsWith("#T:")) {
String[] input = line.split("#T:| ");
String testPaperNum = input[1];
String[] items = Arrays.copyOfRange(input, 2, input.length);
Textpaper textpaper = new Textpaper();
for (String item : items) {
String[] item1 = item.split("-");
String num = item1[0];
int score = Integer.parseInt(item1[1]);
textpaper.setSubject(num, subjectMap.get(num), score);
}
textpaperMap.put(testPaperNum, textpaper);
}
else if (line.startsWith("#S:")) {
String[] input = line.split("#S:| #A:");
String testPaperNum = input[1].split(" ")[0];
studentMap.put(input[1].split(" ")[0], informationMap.get(input[1].split(" ")[1]));
String[] answers = Arrays.copyOfRange(input, 2, input.length);
if (answerMap.containsKey(testPaperNum)) {
answerMap.get(testPaperNum).addAnswer(answers);
}
else {
Answer answer = new Answer();
answer.addAnswer(answers);
answerMap.put(testPaperNum, answer);
}
}
else if (line.startsWith("#X:")) {
String[] input = line.split("#X:")[1].split("-");
for (String information : input) {
String[] studentinfo = information.split(" ");
String studentID = studentinfo[0];
String name = studentinfo[1];
informationMap.put(studentID, new Information(studentID, name));
}
}
else if (line.startsWith("#D:")) {
String[] input = line.split("#D:");
String num = input[1].split("-")[1].trim();
subjectMap.remove(num);
Iterator<Textpaper> iterator = textpaperMap.values().iterator();
for (Textpaper textpaper : textpaperMap.values()) {
textpaper.getSubjectMap().get(num).setFlag(2);
textpaper.setSubjectScore(num);
}
}
}
for (String testPaperNum : textpaperMap.keySet()) {
Textpaper textpaper = textpaperMap.get(testPaperNum);
if (textpaper.getTotalScore() != 100) {
System.out.println("alert: full score of test paper" + testPaperNum + " is not 100 points");
}
}
for (String testPaperNum : textpaperMap.keySet()) {
Textpaper textpaper = textpaperMap.get(testPaperNum);
Answer answer = answerMap.get(testPaperNum);
if (answer == null) {
System.out.printf("The test paper number does not exist");
System.exit(0);
}
for (String[] answers : answer.getAnswersList()) {
int j = 0;
for (String subjectNum : textpaper.getSubjectMap().keySet()) {
Subject subject = textpaper.getSubjectByIndex(subjectNum);
if (subject != null && j < answers.length && subject.getFlag() != 2) {
System.out.print(subject.getContent() + "~" + answers[j].split("-")[1] + "~");
if (subject.judgeAnswer(answers[j].split("-")[1])) {
System.out.println("true");
subject.setFlag(1);
}
else {
System.out.println("false");
subject.setFlag(0);
}
}
else if (subject.getFlag() == 2){
System.out.println("the question " + subjectNum + " invalid~0");
}
else {
System.out.println("non-existent question~0");
}
j ++;
}
System.out.printf(studentMap.get(testPaperNum).getStudentID() + " " + studentMap.get(testPaperNum).getName() + ": ");
int totalScore = 0;
int i = 0;
for (String subjectNum : textpaper.getSubjectMap().keySet()) {
Subject subject = textpaper.getSubjectByIndex(subjectNum);
if (i < answers.length && subject != null) {
if (subject.judgeAnswer(answers[i].split("-")[1])) {
totalScore += textpaper.getSubjectScore(subjectNum);
}
}
if ((i != textpaper.getSubjectCount() - 1)) {
if (subject.getFlag() == 1 && subject != null) {
if (i < answers.length)
System.out.print(textpaper.getSubjectScore(subjectNum) + " ");
else
System.out.print("0" + " ");
}
else
System.out.printf("0" + " ");
}
else {
if (subject.getFlag() == 1 && subject != null) {
if (i < answers.length)
System.out.print(textpaper.getSubjectScore(subjectNum));
else
System.out.print("0");
}
else
System.out.printf("0");
}
i ++;
subject.setFlag(0);
}
System.out.println("~" + totalScore);
}
}
}
}
class Subject {
private String num;
private String content;
private String answer;
private int flag = 0;
public void setFlag(int num) {
this.flag = num;
}
public int getFlag() {
return flag;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public boolean judgeAnswer(String answer) {
return this.answer.equals(answer);
}
}
class Textpaper {
private LinkedHashMap<String, Subject> subjectMap;
private LinkedHashMap<String, Integer> subjectScore;
public Textpaper() {
this.subjectMap = new LinkedHashMap<>();
this.subjectScore = new LinkedHashMap<>();
}
public void setSubject(String num, Subject subject, int score) {
this.subjectMap.put(num, subject);
this.subjectScore.put(num, score);
}
public void setSubjectScore(String num) {
this.subjectScore.replace(num, 0);
}
public HashMap<String, Subject> getSubjectMap() {
return subjectMap;
}
public Subject getSubjectByIndex(String num) {
return subjectMap.get(num);
}
public int getSubjectScore(String num) {
return subjectScore.get(num);
}
public int getSubjectCount() {
return subjectMap.size();
}
public int getTotalScore() {
int totalScore = 0;
for (int score : subjectScore.values()) {
totalScore += score;
}
return totalScore;
}
}
class Answer {
private List<String[]> answersList;
public Answer() {
this.setAnswersList(new ArrayList<>());
}
public void addAnswer(String[] answers) {
this.getAnswersList().add(answers);
}
public List<String[]> getAnswersList() {
return answersList;
}
public void setAnswersList(List<String[]> answersList) {
this.answersList = answersList;
}
}
class Information {
private String studentID;
private String name;
public Information(String studentID, String name) {
this.studentID = studentID;
this.name = name;
}
public String getStudentID() {
return studentID;
}
public void setStudentID(String studentID) {
this.studentID = studentID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
以下是对代码的复杂度分析


以下是代码的类结构图

答题判题程序-3 在 答题判题程序-2的基础上增加了学生信息与删除题目信息
第三次的PTA作业我并没有写出来,仅仅只是得了几个测试点的分数,所以这就呼应了前面我一直所强调的,所有的问题都在第三次作业爆发出来,代码几乎无法修改、再增加功能,而这时又经课堂上老师针对我们类结构设计问题的讲解,我也是彻底看清楚了我的垃圾代码/(ㄒoㄒ)/~~
看这次代码的复杂度分析,Main类Avg Complexity已经到达了41 !!这也正是极限了,我已经无法再对代码进行修改、增加功能。所以我的代码也就止步于此了,对于我来说这也是完全的失败😔。但这也让我重新正视类结构设计的重要性。
(先闭口不谈这次失败的代码)通过类结构图可以知道,这里新增了一个Information信息类,用来储存学生的信息(而删除学生信息却还是写在Main类中😭,我甚至没有给它定义一个新的方法🤬。到这里也就知道我的代码差不多就要完蛋了),删除题目信息同样是用了if条件语句、startsWith()来判断类型并使用正则表达式来处理字符串。
第三次作业总体来说是失败的,因为先前毫无可扩展性的代码,致使增加功能尤为困难(第二次作业增加的功能其实是硬凑上去的,严格意义上并不能叫扩展功能),这也使我认识到一个好的类结构设计有多么重要。
三、采坑心得
第一阶段的PTA作业中,踩的最大的坑就是类结构设计了,不合理的类结构影响的将会是整个代码以及后续的维护和扩展。
还有就是正则表达式,在使用正则表达式前使用的各种if判断语句令人眼花缭乱、精神崩溃,学习正则表达式后都使用类似String[] input = inputs.split("#N:| #Q:| #A:");的代码来分割处理字符串,
在第二次作业中,输入数据时可能会有多张答卷,而我的Answer类为:
点击查看代码
class Answer {
private String[] answer;
public Answer(int n) {
this.answer = new String[n];
}
public void setAnswer(int i, String answer) {
this.answer[i] = answer;
}
public String[] getAnswer() {
return answer;
}
}
因此将answer放入List中即可实现多张答卷输入,代码如下:
点击查看代码
class Answer {
private List<String[]> answersList;
public Answer() {
this.setAnswersList(new ArrayList<>());
}
public void addAnswer(String[] answers) {
this.getAnswersList().add(answers);
}
public List<String[]> getAnswersList() {
return answersList;
}
public void setAnswersList(List<String[]> answersList) {
this.answersList = answersList;
}
}
第三次作业中发现的坑其实在第一次作业时就已经踩进去了,直到第三次作业才发现自己已经在坑底。类结构设计真的很重要!
四、改进建议
个人认为,最需要改进的就是类结构,后面阶段的作业要重新进行类设计,尽量设计出好的类结构,使代码有强的可维护性、可扩展性、可维护性。
其次,需要多学习使用类似于正则表达式、HashMap、List接口等编译软件自带的类,更方便我们编写代码的进程。
例如第二次作业中,使用HashMap类来给题目和试卷的编号进行绑定。
对于答题判题程序,需要将各个功能分隔开,例如处理字符串单独设一个类、判断答案正确设一个类等等,坚持做到单一职责原则。
五、总结
在这三次作业中,我还是学到了挺多的知识:
1、正则表达式:可以帮助我们快速、方便地处理字符串,提高代码的效率和可读性。
2、HashMap集合类:是一种高效、灵活的集合类,可以帮助我们快速、方便地存储和查询数据。
3、ArrayList集合类:可以存储多个元素、可以动态扩展、可以很方便的存储和查询数据。
4、更加熟悉了Java:编程需要注意语法规范、代码风格、异常处理、安全性、可维护性、封装性、可扩展性、可读性等。
5、类结构设计:最重要的一点,好的类结构设计可以使软件更加易于理解、开发和测试,并且可以更好地适应未来的需求变化。
正则表达式有很多语法还需要进一步研究学习,最最重要的是类结构设计,还需要不断地练习!再练习!只有一个好的类结构设计才能写出好的代码!