Iterative Development of a Java Quiz Assessment System
The development process of the quiz assessment system spans three distinct phases, evolving from basic question-answer validation to a comprehensive exam management tool supporting multiple papers, student registration, and dynamic content modification.
Phase 1: Basic Question-Answer Validation
The initial implementation focuses on establishing the core object-oriented structure. The system reads a set number of questions, followed by a list of answers. The primary logic involves parsing specific input formats (e.g., #N:, #Q:, #A:) and comparing submitted answers against standard answers.
The design utilizes a Question class to encapsulate the problem statement and the correct answer. An ExamPaper acts as a container, while an AnswerSheet handles the logic of storing user responses and performing comparisons.
Core Implementation:
import java.util.*;
class QuizItem {
private final int id;
private final String content;
private final String correctAnswer;
public QuizItem(int id, String content, String correctAnswer) {
this.id = id;
this.content = content;
this.correctAnswer = correctAnswer;
}
public boolean validate(String response) {
return correctAnswer.equalsIgnoreCase(response.trim());
}
public String getContent() { return content; }
public int getId() { return id; }
}
class SubmissionHandler {
private final Map<Integer, QuizItem> questionBank = new HashMap<>();
private final List<String> userResponses = new ArrayList<>();
public void loadQuestion(int id, String content, String answer) {
questionBank.put(id, new QuizItem(id, content, answer));
}
public void recordResponse(String response) {
userResponses.add(response.trim());
}
public void processResults() {
StringBuilder output = new StringBuilder();
List<Boolean> outcomes = new ArrayList<>();
// Iterate assuming user responses map to questions 1 to N sequentially
int index = 0;
for (int qId = 1; qId <= userResponses.size(); qId++) {
QuizItem item = questionBank.get(qId);
String answer = userResponses.get(index++);
output.append(item.getContent()).append(" ~").append(answer).append("\n");
outcomes.add(item.validate(answer));
}
System.out.print(output);
for (int i = 0; i < outcomes.size(); i++) {
System.out.print(outcomes.get(i) + (i < outcomes.size() - 1 ? " " : ""));
}
}
}
public class QuizApp {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
SubmissionHandler handler = new SubmissionHandler();
int count = Integer.parseInt(sc.nextLine().trim());
for (int i = 0; i < count; i++) {
String line = sc.nextLine();
// Parsing logic simplified for brevity
String[] tokens = line.split("#N:| #Q:| #A:");
handler.loadQuestion(Integer.parseInt(tokens[1].trim()), tokens[2].trim(), tokens[3].trim());
}
String line;
while (!(line = sc.nextLine()).equals("end")) {
Arrays.stream(line.split("#A:")).filter(s -> !s.trim().isEmpty()).forEach(handler::recordResponse);
}
handler.processResults();
}
}
Phase 2: Multi-Paper and Scoring Logic
The second iteration introduces complexity by decoupling questions from specific exam instances. A TestPaper class is introduced to define a specific set of questions and their associated point values. The system now supports multiple papers, identifies papers where the total score does not equal 100, and calculates scores based on correct answers.
Input parsing becomes more robust, handling different line prefixes (#T:, #S:). The logic handles missing answers by defaulting to null and ignoring surplus answers.
Revised Class Structure:
class PaperDefinition {
private final String paperId;
private final LinkedHashMap<String, Integer> questionScoreMap = new LinkedHashMap<>(); // QuestionID -> Points
public PaperDefinition(String paperId) { this.paperId = paperId; }
public void addQuestion(String qId, int points) {
questionScoreMap.put(qId, points);
}
public int calculateTotal() {
return questionScoreMap.values().stream().mapToInt(Integer::intValue).sum();
}
public Set<String> getQuestionIds() { return questionScoreMap.keySet(); }
public int getScore(String qId) { return questionScoreMap.getOrDefault(qId, 0); }
public String getId() { return paperId; }
}
class GradingEngine {
private final List<QuizItem> questionPool;
private final List<PaperDefinition> papers;
private final List<String[]> submissions; // PaperID + Answers
public GradingEngine(List<QuizItem> qPool, List<PaperDefinition> pList, List<String[]> subs) {
this.questionPool = qPool;
this.papers = pList;
this.submissions = subs;
}
public void run() {
// Check total scores
for (PaperDefinition p : papers) {
if (p.calculateTotal() != 100) {
System.out.println("alert: full score of test paper" + p.getId() + " is not 100 points");
}
}
// Process each submission
for (String[] sub : submissions) {
String pId = sub[0];
PaperDefinition paper = papers.stream().filter(p -> p.getId().equals(pId)).findFirst().orElse(null);
if (paper == null) {
System.out.println("The test paper number does not exist");
continue;
}
List<Integer> scores = new ArrayList<>();
int total = 0;
Iterator<String> answerIter = Arrays.stream(sub).skip(1).iterator();
for (String qId : paper.getQuestionIds()) {
String userAns = answerIter.hasNext() ? answerIter.next() : "answer is null";
QuizItem item = questionPool.stream().filter(q -> q.getId().equals(qId)).findFirst().orElse(null);
if (item == null) continue; // Logic for non-existent questions handled in Phase 3
boolean correct = item.validate(userAns);
System.out.println(item.getContent() + "~" + userAns + "~" + correct);
int pts = correct ? paper.getScore(qId) : 0;
scores.add(pts);
total += pts;
}
System.out.println(scores.toString().replaceAll("[\\[\\],]", "") + "~" + total);
}
}
}
Phase 3: Student Management and Dynamic Modification
The final iteration adds administrative features: student information storage, question deletion, and strict format validation. The system must now handle input errors graceful and prioritize output logic: missing answers take precedence over deleted questions, which in turn take precedence over non-existent questions.
A Student class is introduced. The main controller logic is enhanced to handle regex validation for input lines. When a question is deleted (#D:N-xx), it remains in the system but scores zero and displays an "invalid" message.
Enhanced Logic:
class StudentRecord {
String id;
String name;
public StudentRecord(String id, String name) { this.id = id; this.name = name; }
}
public class ExamSystem {
private static final List<QuizItem> questions = new ArrayList<>();
private static final List<PaperDefinition> papers = new ArrayList<>();
private static final List<StudentRecord> students = new ArrayList<>();
private static final Set<String> deletedQuestions = new HashSet<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
List<String> rawInputs = new ArrayList<>();
while (sc.hasNextLine()) {
String line = sc.nextLine().trim();
if (line.equals("end")) break;
rawInputs.add(line);
}
parseInputs(rawInputs);
processExams();
}
private static void parseInputs(List<String> lines) {
// Logic to sort and parse lines based on prefixes #N, #T, #X, #S, #D
// Includes Regex validation: e.g., "#N:[0-9]+\\s#Q:.+\\s#A:.+"
for (String line : lines) {
if (line.startsWith("#D:N-")) {
deletedQuestions.add(line.substring(5));
}
// ... other parsing logic similar to previous phases
}
}
private static void processExams() {
// Output alerts for paper totals
// Iterate through submissions
// For each answer:
// 1. Check if answer is missing -> "answer is null"
// 2. Check if question ID is in deletedQuestions -> "the question X invalid~0"
// 3. Check if question exists -> "non-existent question~0"
// 4. Else compare and score
}
}