在過去的一個月裡完成了java的前三次大作業對於JAVA的語法以及物件導向程式設計還不臺上手,接下來說前三次大作業。
前三次大作業要是圍繞答題判題系統展開的每次作業都在完善這個程式的功能可以說
1.第一次作業判分功能
在第一次作業階段,核心任務是建立一個能夠接收題目資訊和答題資訊,並能夠進行基本判分的系統。
• 核心功能:
題目處理: 透過 Question 類儲存題目內容和正確答案,為判分提供基礎。
答卷處理: AnswerSheet 類用於收集學生答案,包括學生ID和他們的答案。
判分邏輯: 對學生提交的答案與標準答案進行對比,判斷答題的正確性。
• 技術實現:
使用Java的資料結構如 Map 和 List 來儲存題目和答案。
對輸入的處理採用簡單的字串解析技術,以提取關鍵資訊。
• 挑戰與解決方案:
初始階段主要挑戰是確保所有題目和答案能正確錄入並能進行準確判分。
解決方案是透過單元測試確保每部分輸入正確處理和輸出預期結果。
2.第二次作業管理與總分校驗
第二次作業引入試卷的概念,增加了試卷與題目的關聯管理以及試卷總分的驗證。
• 新增功能:
試卷類的引入: TestPaper 類管理一張試卷中的題目和各題的分值。
總分驗證: 檢查每張試卷的總分是否為100分,如果不是則發出警告。
• 技術深化:
擴充套件了對輸入的解析,包括處理更復雜的字串格式和關聯資料。
引入邏輯判斷試卷總分是否合規。
• 挑戰與解決方案:
確保試卷中的題目與題庫中題目正確關聯,並正確計算總分。
透過逐項新增題目到試卷並實時計算總分來解決問題。
3.第三次作業的錯誤管理和資料完整性
第三次作業不僅增加了對學生資訊的管理,還增強了對錯誤和資料完整性的管理。
• 擴充套件功能:
學生資訊管理: 增加對學生的資訊處理,使得每張答卷都能與具體學生關聯。
題目刪除功能: 允許從系統中刪除題目,即使這些題目已經被試卷引用。
• 健壯性提升:
引入錯誤處理機制,如輸入格式錯誤、不存在的試卷號或學生號等。
對被刪除的題目在輸出時提供特別提示,如"題目無效"。
• 挑戰與解決方案:
處理輸入的複雜性和資料的一致性問題,如確保即使題目被刪除,相關的試卷仍能正確處理。
採用資料驗證和異常處理策略,增強系統的健壯性和使用者的錯誤反饋。
程式碼分析
第一次作業:
類圖:
順序圖:
解析輸入邏輯:parseInput 方法
try {
if (line.startsWith("#N:")) {
// 解析題目資訊
String[] parts = line.split(" #Q:| #A:");
int questionId = Integer.parseInt(parts[0].substring(3));
String questionContent = parts[1];
String answer = parts[2];
questions.put(questionId, new Question(questionContent, answer));
} else if (line.startsWith("#T:")) {
// 解析試卷資訊
String[] parts = line.substring(3).split(" ");
int paperId = Integer.parseInt(parts[0]);
Paper paper = new Paper(paperId);
for (int i = 1; i < parts.length; i++) {
String[] questionParts = parts[i].split("-");
int questionId = Integer.parseInt(questionParts[0]);
int score = Integer.parseInt(questionParts[1]);
paper.addQuestion(questionId, score);
}
papers.put(paperId, paper);
} else if (line.startsWith("#X:")) {
// 解析學生資訊
String[] studentInfos = line.substring(3).split("-");
for (String studentInfo : studentInfos) {
String[] parts = studentInfo.split(" ");
String studentId = parts[0];
String name = parts[1];
students.put(studentId, new Student(name));
}
} else if (line.startsWith("#S:")) {
// 解析答卷資訊
String[] parts = line.split(" ");
int paperId = Integer.parseInt(parts[0].substring(3));
String studentId = parts[1];
AnswerSheet answerSheet = new AnswerSheet(paperId, studentId);
for (int i = 2; i < parts.length; i++) {
if (parts[i].startsWith("#A:")) {
String[] answerParts = parts[i].substring(3).split("-");
int questionIndex = Integer.parseInt(answerParts[0]);
String answer = answerParts[1];
answerSheet.addAnswer(questionIndex, answer);
}
}
answerSheets.add(answerSheet);
} else if (line.startsWith("#D:N-")) {
// 解析刪除題目資訊
int questionId = Integer.parseInt(line.substring(5));
deletedQuestions.add(questionId);
if (questions.containsKey(questionId)) {
questions.get(questionId).setValid(false);
}
} else {
throw new IllegalArgumentException("wrong format:" + line);
}
} catch (Exception e) {
System.out.println("wrong format:" + line);
}
}
優點:
分支清晰:根據不同的輸入字首 (#N:, #T:, #X:, #S:, #D:N-),程式透過 if-else 結構對不同型別的輸入進行處理,邏輯簡單明瞭,便於理解。
資料解析合理:對於題目、試卷、學生資訊、答卷和刪除題目,使用字串拆分 (split) 的方式,從輸入中提取資料,結合具體的識別符號提取相關欄位,能夠有效處理複雜格式。
良好的容錯性:每個分支都使用 try-catch 捕獲異常,如果遇到格式錯誤的輸入,程式可以避免崩潰,並輸出提示,繼續處理後續輸入。
可擴充套件性強:基於輸入字首的模式(如 #N:, #T:),可以很容易新增新的輸入處理邏輯,增加系統功能。
缺點:
輸入格式依賴性高:程式碼對於輸入格式的假設比較嚴格,輸入格式的稍微變化(如少一個空格或格式不一致)可能會導致解析失敗並輸出 "wrong format" 提示,但不能提供詳細的錯誤資訊幫助除錯。
程式碼冗餘:多個 if-else 分支都有類似的字串解析操作,比如分割字串、取子串等,雖然功能不同,但某些部分可以進一步重構和最佳化,減少重複程式碼。
錯誤處理簡單:catch 中僅簡單地輸出錯誤提示,對於不同的異常型別(如空輸入、非法格式等)沒有詳細的區分,也沒有額外提示如何修復輸入錯誤。
硬編碼解析邏輯:解析邏輯透過直接字串切割和硬編碼索引來提取資料,雖然簡單但不靈活,特別是當輸入格式發生變化時(如題目資訊不止兩部分)會變得難以維護和擴充套件。
成績處理邏輯:processResults 方法
static void processResults() {
// 計算每張試卷的總分並生成警示資訊
for (Map.Entry<Integer, Paper> entry : papers.entrySet()) {
Paper paper = entry.getValue();
int totalScore = paper.getQuestions().values().stream().mapToInt(Integer::intValue).sum();
if (totalScore != 100) {
System.out.println("alert: full score of test paper" + entry.getKey() + " is not 100 points");
}
}
// 處理每個學生的答卷
for (AnswerSheet answerSheet : answerSheets) {
Paper paper = papers.get(answerSheet.getPaperId());
if (paper == null) {
System.out.println("The test paper number does not exist");
continue;
}
Student student = students.get(answerSheet.getStudentId());
if (student == null) {
System.out.println(answerSheet.getStudentId() + " not found");
continue;
}
// 記錄每個題目的結果和得分
List<String> questionResults = new ArrayList<>();
List<String> scoreResults = new ArrayList<>();
int totalScore = 0;
for (Map.Entry<Integer, Integer> questionEntry : paper.getQuestions().entrySet()) {
Integer questionId = questionEntry.getKey();
Integer score = questionEntry.getValue();
// 檢查題目是否存在於題庫中
Question question = questions.get(questionId);
if (question == null) {
// 題目不存在的情況
questionResults.add("non-existent question~0");
scoreResults.add("0");
} else if (!question.isValid()) {
// 題目無效(已刪除)的情況
questionResults.add("the question " + questionId + " invalid~0");
scoreResults.add("0");
} else {
// 題目有效,繼續檢查學生作答情況
String studentAnswer = answerSheet.getAnswers().getOrDefault(questionId, "answer is null").trim();
if (studentAnswer.equals("answer is null")) {
// 學生未作答該題目
questionResults.add("answer is null");
scoreResults.add("0");
} else {
// 學生作答,判斷是否正確
boolean correct = studentAnswer.equals(question.getAnswer());
int obtainedScore = correct ? score : 0;
totalScore += obtainedScore;
questionResults.add(question.getContent() + "~" + studentAnswer + "~" + (correct ? "true" : "false"));
scoreResults.add(String.valueOf(obtainedScore));
}
}
}
// 輸出題目的結果
for (String result : questionResults) {
System.out.println(result);
}
// 輸出學生總成績及題目得分,格式為: 學號 姓名: 每題得分 總分~總分
String scoreString = String.join(" ", scoreResults); // 將每題得分用空格連線
System.out.println(answerSheet.getStudentId() + " " + student.getName() + ": " + scoreString + "~" + totalScore);
}
}
優點:
試卷總分驗證:系統首先計算每張試卷的總分並檢查是否為 100 分,提供合理的警示機制,如果分值設定有誤,可以及時發現問題。
答卷處理細緻:處理每個學生的答卷時,針對不同情況提供詳細判斷,包括題目不存在、題目被刪除、學生未作答、學生答題錯誤等。這種細化的判斷使得程式對各種答題場景都有較好的處理能力。
實時輸出結果:程式在處理每個答卷時,會實時輸出每道題的答題情況以及每個學生的成績,這有助於快速獲取結果,便於除錯。
答案與分數分離:將學生的每題答題情況與得分分別儲存在兩個不同的列表 (questionResults 和 scoreResults),使得程式碼邏輯清晰,輸出時可以靈活組合展示資訊。
缺點:
複雜性增加:為了處理各種可能的輸入情況(題目不存在、題目無效、學生未作答等),程式碼中存在多層巢狀 if-else,導致程式碼的複雜度較高,後續維護難度可能較大。
硬編碼邏輯:對於題目的正確性判斷和分數計算,都採用了硬編碼的方式,沒有提供靈活的評分機制。如果以後需要擴充套件,例如引入部分正確、題目權重等,修改現有程式碼的成本會比較大。
缺少統一的錯誤處理機制:每種錯誤(如無效題目、未作答)都在分支內單獨處理,雖然能應對各種情況,但沒有統一的錯誤處理機制。如果增加更多種類的錯誤型別,程式碼將變得更難維護。
程式碼冗餘:對於題目結果和分數的記錄與輸出,雖然分離了答題結果和得分,但程式碼仍然存在一定冗餘,例如每個條件分支都需要分別處理 questionResults 和 scoreResults,可以考慮進一步簡化。
在提交原始碼的過程中,遇到了幾個常見的問題:
字串解析問題:在解析輸入資料時,由於輸入格式稍有差異或空格不規範,導致 split() 的結果不如預期。例如,輸入中的多餘空格或輸入的某些部分缺少必要的標誌符,導致解析異常。
解決方案:透過使用 trim() 方法清除多餘的空格,並新增更多的格式校驗和異常處理。
資料丟失問題:在解析學生答卷時,由於某些題目ID沒有正確對映到題庫,導致後續評分時出現空指標異常。
解決方案:增加對題目ID的合法性檢查,並在答卷處理時進行更嚴格的資料驗證。
邊界情況處理不足:例如,有些學生未作答某些題目時,沒有給出明確的處理邏輯,導致程式輸出出現不一致。
解決方案:完善了預設值的處理,對於未作答的題目,給予零分處理,並在輸出時增加了相應提示。
第二次作業:
類圖: