OOP1-3作业集总结性Blog

这三次题目集的题量分别为5,4,3,题量逐渐减少,但难度逐渐上升。主要涉及的知识点有类的封装性,容器的使用,API的调用,正则表达式的用法。

对于三道 答题判题程序 的设计,在看了设计建议后,才发觉自己设计的类过于粘稠,存在一个类中总是通过简单的getXX的粗暴方法来利用别的类中的变量,使得该类与多类连接紧密,不仅使代码不易看懂,还使得代码在修改一个类时往往还需要修改别的类·,导致类不易扩展。

接下来,简单分析下三次判题程序中我与设计建议间的区别。

第一次作业

SourceMonitor分析如下:


UML类图如下:

设计与分析:

设计:

设计建议中类的功能:

(1)题目类(Question)
private int num;// 题号

private String content; // 题目内容

standardAnswer;// 标准答案

boolean matchingStandardAnswers(String answer);//判断是否符合标准答案
(2)试卷类(TestPaper)
int questionNum;// 题目数量

Question[] questions;//题目清单

void inputQuestion(int num, Question question);//添加一道题目

void printQuestions();//输出题目的内容~标准答案

boolean markQuestion(int num, String answer);//判断第 num 题的正确性。
(3)答卷类(AnswerPaper)
TestPaper paper;//试卷信息

String[] answers;//每一题的答案信息

boolean marks;//每一题的判题结果(对/错)

void printQ_A(int num);//输出第 num 题的题目和答案(卷面答案,非标准答案)

boolean getJudge(int num);//获得第 num 题的判题结果

我的类的功能:

(1)题目类(Question)
private int  id;
private String content;
private String standardAnswer;
方法
一系列get set
(2)答案类(Answer)
private int num=0; //用来判断是第几题的答案
private String answer; //回答的答案
方法
一系列get set
(3)试卷类(Test)
private int num=100; //定义试卷大小
private Question [] test=new Question[this.num];//将一系列问题(question)数组定义为test
private Answer [] answers =new Answer[num];//回答数组
public int[] yes=new int [num];//判断答案对错数组

public  void pigai();//判断对错且输出

分析:

我的设计:耦合度较高的设计

我的设计中,试卷类直接包含了题目类和答案类的数组,并且试卷类本身负责对学生答卷的判定。

优点:

  1. 简单直观: 这种设计简单明了,直接在试卷类中管理了所有的题目和答案。
  2. 快速实现: 对于小规模的问题,这种设计可能更容易快速实现。

缺点:

  1. 耦合度高: 试卷类与题目类和答案类之间的耦合度很高,试卷类直接依赖于题目类和答案类的实现细节。这意味着如果需要修改题目类或者答案类,可能会影响试卷类的代码。
  2. 单一职责原则违反: 试卷类不仅负责试卷的管理,还负责对学生答卷的判定。这违反了单一职责原则,使得试卷类的功能过于集中,难以维护和扩展。
  3. 灵活性差: 这种设计固定了试卷的大小,并且试卷类直接定义了题目数组和答案数组,导致缺乏灵活性。

设计建议:耦合度较低的设计(组合关系)

设计建议中,试卷类通过组合关系包含了题目类和答卷类的对象,而试卷类本身只负责试卷的生成和管理,而对学生答卷的判定则交给答卷类,符合了开闭原则。

优点:

  1. 低耦合度: 试卷类与题目类和答卷类之间的耦合度较低,试卷类不直接依赖于题目类和答卷类的实现细节。这使得修改题目类或者答卷类不太可能影响试卷类的代码。
  2. 单一职责原则: 每个类负责自己的职责,试卷类只负责试卷的生成和管理,答卷类负责学生答卷的管理和判定,这符合单一职责原则,使得代码更易于维护和扩展。
  3. 灵活性高: 这种设计更灵活,试卷的大小和题目与答案的关联方式可以根据需要动态调整。

缺点:

  1. 稍微复杂: 相对于第一种设计,这种设计可能稍微复杂一些,需要更多的类来实现相同的功能。但是,这种复杂性可以通过良好的设计和抽象来降低。

改进建议:

我对单一职责原则的理解还停留在c语言是每个函数职责的单一,而忽略了java中所讲的单一职责原则其实更加注重的是每个类功能应该是单一的。

虽然我在设计中用了三个类,但对封装性的使用很差,其中两个类的功能只有储存变量而不含任何方法,导致批改题目正误和改分的任务都集中在了试卷类中。

所以,应该在类中就把该类中变量可能被使用的方法用方法封装起来,如这题中因为题目类内中有变量standardAnswer就可以将判断正误的功能加在题目类上。而AnswerPaper类中可以通过就访问题目类中的判题方法来设置判题。

出现错误:

输出应该输出学生的作答,但我输出的是标准答案。

采坑心得:

1.应当留意PTA中输入测试样例时所出现的蓝线提示,这蓝线会提示输出的不同之处,

2.当看不懂题目使可以从输出样例寻找灵感。

第二次作业

SourceMonitor分析如下:

UML类图如下:

设计:

我的类设计:

(1)题目类(Question):将整个题目的信息封装
private int  id;//题目集中试卷题号

private String content;//题目

private String standardAnswer;//标准答案

方法
一系列get set
(2)答案类(Answer):
private int num=0; //用来判断是第几题的答案

private String answer; //回答的答案

方法
一系列get set
(3)试卷类(Test)
private final int[] titie = new int[100];//题目在题目集的位置 如:titie[1]=3,说明试卷第一题是题目集中第3题

private final int[] grade = new int[100];//各个题目成绩

private int sumGrade = 0;//试卷总分

private int numTest = 0;//题目数量

private final int[] yes = new int[100];//判断题目是否正确

//** 可以看出这个方法的if深度和与答案类和题目类的耦合度很高*/
public  void pigai();//判断对错且输出

public void standard(int n);//判断试卷是否为100分

设计建议类设计:

题目类(Question): 用于保存一道题目的信息以及处理的方法。(与系列 1 相同,无变化)
private int num;// 题号

private String content, // 题目内容

standardAnswer;// 标准答案

boolean matchingStandardAnswers(String answer);//判断是否符合标准答案
试卷题目类(Question_Paper新增):用于保存试卷中的题目信息。由于试卷中的题目序
号与题目本身的题号不一致。且题目在不同试卷中的分值可能不一样,因此,设计试卷题目
类。
int p_q_num;//试卷中题目的顺序号

Question question;//题目类的对象,保存题目信息

int question_score;// 题目分值

int judge_markAnswer(String answer); // 判断题目得分
试卷类(TestPaper):保存和处理一张试卷的信息以及处理的方法,
int questionNum;// 题目数量

Question[] questions;//题目清单

void inputQuestion(int num, Question question);//添加一道题目

void printQuestions();//输出题目的内容 + ~

boolean markQuestion(int num, String answer);//判断第 num 题的正确性。--虽然感觉这个方法在这道题上没被用上,但其有存在的意义。可能老师写这个方法时,是按现实中写程序的思路来写的

int sum;//保存总分 (新增)

int questionQuantity;//保存题目数量 (新增)

int getSum();//获得总分(新增)
答案类(Answer 新增):用于保存答卷中一道答案的信息。
Question_Paper question;

String answer;

boolean mark;// 每一题的正确性

int score=0;// 每一题的得分

void calScore();//计算得分
答卷类(AnswerPaper):保存和处理一张答卷的信息以及处理的方法
TestPaper paper;//试卷信息

String[] answers;//每一题的答案信息

boolean[] marks;//每一题的判题结果(对/错)(去除)

void printQ_A(int num); //输出第 num 题的题目和答案(卷面答案,非标准答案)
    
boolean getJudge(int num); //获得第 num 题的判题结果
    
void printJudges(); // 输出所有的得分以及总分,以空格分隔(新增)

分析:

题目修改:

本题在输入时增加了(1)储存不同张试卷,(2)分析学生对特定的试卷作答的要求。

在输出时也增加了(3)判断每张试卷是否标准,(4)打印每张学生答题卷的正误(5)打印学生得分情况的要求。

对代码进行的修改:

我:

修改各种正则表达式来满足对各个类中新增的变量的分割要求,在试卷类中的pigai方法中新增解决(4)(5)的代码

在试卷类中新增public void standard(int n);//判断试卷是否为100分方法来解决(3)问题

出现错误:

输出试卷是否标准时应该输出具体是哪张试卷,但我未加以判断。

改进建议:

(1)对答案类进行如下修改:

由于所有答案类(多选题,填空题)都可以有

Question question;//储存指定回答的题目

String answer;

int score=0;// 每一题的得分

void calScore();//判断正误并计算得分

这些变量,方法,这样设计答案类,有利于后面别的类型答案的继承。

(为什么要储存 Question_Paper question 变量)

由于日后可能在题型上对程序进行修改,在Answer类中储存其所回答的题目,就可以通过Question类中的判题方法判题,这时在面对各种题型时都使用calScore来判断题目正误和得分,增强了该程序的鲁棒性。

其次,我觉得为了利于之后对所有试卷题目作答情况进行判断,应该在答卷类中设置一个数组大小为试卷题目数量的答案类数组

(2)对答卷类进行如下修改

TestPaper paper;//试卷信息

Answer[] answers;//每一题的答案信息

void printQ_A(int num); //输出第 num 题的题目和答案(卷面答案,非标准答案)

void JudgeCorrect();//判断所有题目的正误

void printCorrect();//打印所有判题结果。

boolean getJudge(int num); //获得第 num 题的判题结果
    
void printJudges(); // 输出所有的得分以及总分,以空格分隔(新增)

答卷类中内存TestPaper类因为当需要TestPaper类中的题目数量变量来定义answers数组的大小,且可以利用TestPaper类中的void printQuestions();//输出题目的内容 + ~方法来辅助PrintCorrect方法的实现。

(3)对试卷类进行如下修改

int questionNum;// 题目数量

int sum;//保存总分 (新增)

Question[] questions;//题目清单

void addQuestion(int num, Question question);//添加一道题目,由于题目的添加不是按顺序的所以要加num

void printQuestions();//输出题目的内容 + ~

void getQuestion(int num);// 返回试卷上指定题号的题目,在answer类的初始化中可能用上

int getSum();//获得总分(新增)

将原本我设计中在试卷类中的pigai方法删去,并将判题的任务分给每个答案类减少了试卷类和答案类的耦合性。

且当程序要对多种题型的批改时,原本单一的Pigai方法将需要通过多重if来判断用哪种判题方法来批改String answer,修改后

由于题目答案是一一对应的,就不需要if判断了,使代码易于扩展。

第三次作业

SourceMonitor分析如下:

UML类图如下:

设计与分析:

我的类设计:

Answer类:

private final String[] answers;
private int indexContents;
private final int indexTest;
private int numReplies;
private String studentNumber;
Answer.extractStudentNumber()//用于分割字符串
Answer.getAnswers()
Answer.getIndexTest()
Answer.getStudentNumber()

AutomaticGrading类:

private int numTest;//试卷的张数
private int numAnswer;//答卷的张数
private String[] wrong;//储存错误信息--用Array数组可能更好
private int numWrong;//储存错误信息的数量
public static int judgeType(String s);//判断是哪种类型信息
AutomaticGrading.addNumAnswer()
AutomaticGrading.addNumTest()		
AutomaticGrading.addWrong()	//添加输入错误的信息
AutomaticGrading.judgeStandard()//判断输入格式的标准程度
AutomaticGrading.printWrong()//打印格式错误的信息

Question类:

Question.getContent()			
Question.getExist()			
Question.getStandardAnswer()		
Question.Question()			
Question.setExist()	//设置题目是否被删除	

Student类:

Student.getID()				
Student.getName()			
Student.Student()	

Test类:

private final int[] indexTitie; //储存试卷内题目的变量
private final int[] grade;//储存试卷内每题分数的变量
private int sumGrade;//试卷总分
private int numTitie;//试卷总题数
//判断这张试卷是否满分100
public void standard(int n)
//判断题目是否存在
private boolean judgeProblemExist(int index, Question[] questions) 
//判断题目是否被删除
private boolean judgeProblemInvalid(int index, Question[] questions) //判断问题是否有效
private void correctTestHelp(int i, Question[] questions, Answer answer)//判断问题是否正确
public void correctTest(Question[] questions, Answer answer) //
public void rate() 

虽然这次设计时单独将分割字符串的任务交给了AutomaticGrading类,但还是没改掉Test类与Answer,Question类耦合程度高的情况。

出现错误:

在各种信息中的正则表达式的切割上有问题

其实这后面几个答案错误都是因为正则表达式的书写出现错误。

比如一开始题目信息中我的正则表达式无法分割 asdf asdf 这样的字符串

将判断题目的正则表达式换为

String pattern = "#Q:\\s*(.*?)\\s*#A:\\s*(.*)";

在答案信息输入时答案可以存在 asdf asdf 这样的格式且在比较答案正误时应该取出前后的空格

改进建议:

在第二次作业改进的基础上,加上对题目是否作答的判断。

(1)对答案类进行如下修改:

Question question;//储存指定回答的题目

String answer;

boolean mark;//判断是否作答(新增)

int score=0;// 每一题的得分

void calScore();//判断正误并计算得分

(2)对答卷类进行如下修改

TestPaper paper;//试卷信息

Answer[] answers;//每一题的答案信息

void printQ_A(int num); //输出第 num 题的题目和答案(卷面答案,非标准答案)

void JudgeCorrect();//判断所有题目的正误,将正误信息储存在answer类数组中

boolean judgeErrorQuestion()//判断试卷题目是否存在并打印相应信息

void printCorrect();//打印所有判题结果。

boolean getJudge(int num); //获得第 num 题的判题结果
    
void printJudges(); // 输出所有的得分以及总分,以空格分隔(新增)

(3)AutomaticGrading类

private QuestionSet questionSet;

private TestPaperSet testPaperSet;

private AnswerPaperSet answerPaperSet;

private StudentSet studentSet;

public void input_Q(String line);//解析题目信息的输入

public void inputTest(String line);//解析试卷信息的输入

public void inputAnswer(String line);//解析答案信息的输入

public void inputStu(String line); //解析学生信息的输入

因为分割字符串的方法很容易变化,将分割方法冲类中分离出去,但正则表达式需要改变时,可以减少对其相应类不必要的修改

(4)JugdeInput类

用于判断输入是否符合要求,且输出错误的类信息,单独分成类是为了减少AutomaticGrading类的功能

综合性总结:

(1)让我学习到了正则表达式的用法。

(2)初步领略了设计模式的6大设计原则,通过观察设计建议,学习到了通过类间的组合关系来设计类可以减低类间的耦合性,虽然更复杂但更符合软件设计中要求程序易于扩展的需求。

  1. 单一职责原则 (Single Responsibility Principle, SRP):一个类应该只有一个引起变化的原因。换句话说,一个类或模块应该只负责一项功能。----------理解了单一原则不仅仅是c语言中函数职责的单一更是对类的单一职责做出限定
  2. 开闭原则 (Open/Closed Principle, OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着可以通过添加新代码来扩展系统的功能,而无需修改已有代码。----------通过组合关系设计类,在满足程序需求上更符合开闭原则
  3. 里氏替换原则 (Liskov Substitution Principle, LSP):子类应该能够替换掉父类并且不影响程序的正确性。换句话说,子类应该能够替代父类的行为。
  4. 依赖倒置原则 (Dependency Inversion Principle, DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于具体实现,而具体实现应该依赖于抽象。
  5. 接口隔离原则 (Interface Segregation Principle, ISP):一个类不应该强迫它的用户依赖于他们不需要的接口。换句话说,一个类应该只提供它的用户需要的方法,而不应该提供不必要的方法。
  6. 最少知识原则 (Least Knowledge Principle, LKP,也称为迪米特法则):一个对象应该对其他对象有最少的了解。对象之间的耦合越低,系统的灵活性、复用性和可维护性就越好。----------一个对象应该在自己所含成员基础上设计方法,减少对其他类getXX方法的直接调用(直接调用get类方法一个类知道了另一个类中的变量信息)

通过这三次作业,我也发现了自己对减少类间耦合性,增强程序鲁棒性能力上的不足,应该多使用组合关系设计类,接下来应该好好研读下设计模式相关的书籍,提升自己减低程序复杂度的能力

建议及意见

提高所发布设计建议中UML类图的准确性,对设计建议中类内函数的作用做更详细的注释,比如说在解决什么问题的时候用的上,这样设计的好处等等,有助于学生自己领会这样设计的益处,更利于学生模仿相应类设计。

posted @ 2024-04-21 21:56  林子炫  阅读(48)  评论(0)    收藏  举报