前言
在過去的幾周內,我們完成了題目集1至3的練習,涉及多個知識點和程式設計技巧。整體來說,這三次題目集共包含了12道題目,題目難度逐步增加。
題量:
題目集1(5道題),題目集2(4道題),題目集3(3道題)。
難度:
題目集1:稍微困難,適合剛入門的學習者,涵蓋的知識點有類的定義、物件的建立、封裝、構造方法的使用、資料訪問、字串處理以及方法重寫等知識點,透過第一次題目集,可以更好地理解這些概念。
題目集2:中等難度,涉及較複雜的資料結構和基本演算法,涵蓋的知識點與第一次大致相同,但加強了對字串處理的知識點的應用。
題目集3:較高難度,需要綜合運用多種知識點,特別是最後一題,考驗了對設計模式的規劃,加強對資料結構對映的應用,以及輸入輸出處理難度加大。 透過這三次練習,我對程式設計的思維方式有了更深的理解,也提高了編寫程式碼的能力。
設計與分析
題目集1 7-5 答題判題程式-1
設計
思路
類圖
使用者使用答題程式-1順序圖
分析
報表資料
從圖中資料可以得知,除 Avg Depth稍稍偏高外其它各項基本正常。
題目類
1 class Question { 2 private int id; 3 private String content; 4 private String standardAnswer; 5 public Question(int id, String content, String standardAnswer) { 6 this.id = id; 7 this.content = content.trim(); 8 this.standardAnswer = standardAnswer.trim(); 9 } 10 public boolean checkAnswer(String answer) { 11 String a=answer.trim(); 12 return standardAnswer.equals(a); 13 } 14 }
每個題目都有相應的題目Id、題目內容以及題目標準答案,所以我們在題目類中設計三個私有資料分別儲存題目Id、題目內容以及題目標準答案,同時我們應該使用有參建構函式的方法來儲存每個題目。由於對於答卷中的每個題目答案我們都需要將其與相應題目的標準答案進行比較,所以我們可以直接在題目類中設計一個 boolean 型別checkAnswer 方法進行返回判斷結果,快速得到結果。
試卷類
1 class Exam { 2 private List<Question> questions; 3 public Exam() { 4 this.questions = new ArrayList<>(); 5 } 6 public void addQuestion(Question question) { 7 questions.add(question); 8 } 9 public List<Question> getQuestions() { 10 return questions; 11 } 12 public Map<Integer, Question> getQuestionMap() { 13 Map<Integer, Question> map = new HashMap<>(); 14 for (int i = 0; i < questions.size(); i++) { 15 Question q = questions.get(i); 16 map.put(q.getId(), q); 17 } 18 return map; 19 } 20 }
每一張試卷都有引用題目類中的若干已儲存的題目資訊,由於這道題沒有要求我們判斷引用的題目是否存在題目類中,而且我們事先並不知道每張試卷都分別有哪些題目,所以我們可以使用 ArrayList 動態陣列對試卷中題目資訊進行儲存,然後我們可以透過獲取 ArrayList 的長度從而獲得題目數量資訊。我們在getQuestionMap()方法中使用了 Map介面和 HashMap<>() 實現,用於儲存問題 ID 與問題物件之間的對映關係,public Exam()這是一個無參建構函式,用於初始化 Exam 類的例項,並在構造時建立一個新的ArrayList物件,最後使用List<Question>和 Map<Integer, Question> 的返回型別,允許呼叫者獲取類內部的集合資料。
答卷類
每個題目對應的答案都是在一張試卷上作答,所以我們在答卷中設計一個試卷型別的屬性對相應試卷資訊進行儲存,同時對於試卷中的每個題目,我們需要知道其答卷中的作答,對於每個作答我們在判斷其是否正確,所以我們又設計了建立新的 ArrayList
物件用於 answers
和 results
將答案以及結果進行儲存,並且我們使用了 ArrayList 便於動態更新這些資訊。我們在答卷類中設計了一個evaluateAnswers方法,透過其呼叫試卷類中方法獲取題目以及使用者的答案資訊,然後再呼叫題目類中判斷方法,對每個答案進行判斷,並將判斷結果儲存到結果中。由於我們最後輸出的資訊與每張答卷有關,所以我們在答卷類中設計了printResults(Map<Integer, Question> questionMap)方法,列印每個問題的內容和使用者的回答,同時列印每個答案的正確性結果。最後只需要在測試類中呼叫這個方法對每張答卷最後的結果進行列印。AnswerSheet(Exam exam)建構函式接收一個 Exam
物件並初始化 AnswerSheet
的例項,同時建立新的 ArrayList
物件用於 answers
和 results。
測試類
測試類用於資料讀取,由於題目,答卷類是輸入一行的一大段字串來代表題目的資訊,所以我們應該透過正規表示式來將字串分解,將#N,#A中的資訊提取到對應儲存空間中進行儲存。並且呼叫答卷中的方法將結果列印出來。
題目集2 7-4 答題判題程式-2
設計
思路
題目集2相對於題目集1在試卷資訊中增加了分數,以及多張試卷的資訊,因此我們需要在試卷類中增加相應的屬性對分數以及對試卷的Id進行儲存,同時設計合適的方法對分數進行判斷,同時題目輸入的資料每行對應不同資訊,每行開頭使用不同的字串進行各類資訊的區分輸入,所以我們可以新加一個QuizSystem類用於處理各類資訊儲存,並且將各類資訊字串進行處理,列印最後的結果。
類圖
使用者使用答題程式-2順序圖
分析
資料包表
從圖中資料可以得知,除 Max Complexity偏高外其它各項基本正常。
題目類
此次作業中題目類與第一次作業題目類基本相同,我將判斷方法封裝進了新加的QuizSystem類。
試卷類
相較於第一次作業,這次作業中試卷中每個題目都增加了分數資訊,以及試卷Id,並且相同的題目在不同的試卷中其分數是不相同的,所以我們在試卷類中新增一個分數屬性用於儲存試卷中對應題目的分數資訊,和一個試卷Id資訊用於儲存對應的試卷資訊。由於對於每張試卷我們都需要判斷其總分是否是 100 分,所以我增加了一個addQuestion()方法計算每張試卷的總分。由於我對動態陣列運用不夠熟練以及總是報錯,於是我將第一次作業中試卷類方法全盤改造,從自己會的入手,有時間將會嘗試用之前的方法做出來。
答卷類
與上個改造原因相同,這次只簡單的在類中加入一個構造方法,沒有事先規劃好類中的方法。
答卷系統類
答卷系統類中,我們首先使用processInput()方法將開頭資訊進行比對後,將對應資訊用對應方法進行處理。在每個資訊處理方法中,我們使用不同的正規表示式進行分割字串,然後將對應資訊存入相應的類中。最後我們使用generateOutput()方法將輸出資訊一一列印出來,在generateOutput()方法中,我們需要判斷試卷是否為滿分,試卷是否存在,答卷中使用者是否給出答案,以及將總分和各題得分情況列印出來。由於時間的原因,這個類的設計沒有做到很好,方法頗為臃腫,後續我將會減少該類的方法,將對應方法封裝到各自的類中去。
測試類
在測試類中,輸入測試資料,只需呼叫答卷系統中的方法就能實現程式所需完成的任務,減輕了測試難度。
題目集3 7-3 答題判題程式-3
設計
思路
這次作業相較於題目集2增加了學生姓名、學號,因此我們應該再設計一個類儲存這些資訊,同時在答卷系統類中還需要新增一個處理刪除題目資訊的方法,並且需要知道某個題目是否被刪除,因此我們還需要設計一個狀態屬性來儲存被刪除題目的標識。這次題目集要求我們規範輸入格式,因此我們可以使用正規表示式來判斷輸入,使我們的程式可讀性更高。
類圖
使用者使用答題程式-3順序圖
分析
報表資料
從圖中資料可以得知,各項資料都有點不在正常範圍內,說明此程式碼需要進行改進,在後續作業中我會加強對這些的注意。
題目類
在題目類中,為了能夠儘快查詢某個題目是否被刪除,所以我增加了一個狀態屬性,用於判斷該題目是否被刪除,以及增加了題目編號這一屬性用於判斷對應題目在試卷類中對應答案是否正確。
試卷類
在試卷類中,我新增加了一個屬性,用於題目數量的記錄,因為後續的字串拆分過程中我們需要用到這個屬性來判斷題目數量。
答卷類
與上次作業中的設計大致相同,由於對於每個學號我們都需要找到其對應的學生資訊,所以增加了一個新屬性,記錄答卷人的資訊,用於將學號名字列印出來。
答卷系統類
在答卷系統類中,處理資訊的方法中加入了正規表示式字串匹配過程,這應該寫入一個方法中,但我卻沒有寫入,導致有點像程序導向。
學生類
1 class Student { 2 String id; 3 String name; 4 5 public Student(String id, String name) { 6 this.id = id; 7 this.name = name; 8 } 9 }
學生類包含基本屬性,姓名與學號,以及有參建構函式的方法。
踩坑心得
坑1:題目可以有空格
在第二次作業中,由於沒把第一次作業中題目中可以有空格這個隱藏坑點記住導致我答題程式-2有個測試點總是過不了
例如:
輸入
#N:1 #Q:1+ 1= #A:2
#N:2 #Q:2+2= #A:4
#T:1 1-5 2-8
#S:1 #A:5 #A:22
end
將題目中的答案給去除了。後面請教老師,給我一個輸出資料讓我找自己的錯誤,看到那個空格,如夢初醒。
坑2:答案可以有空格
在答題程式-3中,允許答案中有空格。開始沒有考慮到這個問題,導致輸出的答案錯誤。
例如:
輸入
#N:1 #Q:1+1= #A 2
#N:2 #Q:1+1= #A:2 #B:3
#T:1 1-5 2-2 3-9
#X:20201103 Tom-20201104 Jack
#S:1 20201103 #A:1-5 #A:2-2 3
#D:N-2
end
顯示的為錯誤的輸入,後面重新將正規表示式改為 "^#S:\\w+(\\s+\\w+)?(\\s+(#A:.+$))*?$"即可解決問題。
坑3:
由於自己的疏忽,將人名查詢時總是隻有一個人,其他人都顯示為20201105 not found,由於給出的測試點中沒有給出查詢其他人的測試點,導致我始終沒有找到我的錯誤。後面發現自己的程式碼中查詢學生姓名時始終只查詢第一個人。
這個坑純屬於自己給自己挖坑,自己卻沒有發現。後面增加一個屬性記錄學生數量,查詢次數為學生數時,問題得以解決。
透過上述三個坑點,我瞭解到在處理複雜輸入時,正規表示式是一種強大的工具,可以幫助我們驗證資料格式。然而,設計正規表示式時需謹慎,因為不準確的模式可能導致誤判,將有效輸入識別為錯誤。這不僅會影響使用者體驗,還可能導致資料丟失或錯誤分析。因此,確保正規表示式的準確性和健壯性至關重要。可以透過測試用例和邊界條件反覆驗證,確保其在各種輸入情況下都能正常工作,從而提升資料的可靠性和系統的整體健壯性,以及要對迴圈次數的把控要準確,做題要有連貫性,不能斷斷續續的做題。
改進建議
自己的類中,有些類設計的方法過於繁多,這都是程序導向的思維方式,後續會將各自類中的方法封裝完善,使每個類做好自己的工作。還有類中的方法中有些程式碼過於龐大,應該細化到其他方法中去。在接收到答卷資訊時先將接收到的資訊單獨儲存起來而不進行處理,在最後輸出答卷資訊時我們再統一處理,我們就可以按照正確的格式進行輸出,避免輸入時順序打亂而程式崩潰。
總結
在本階段的三次作業中,我深刻認識到了程式設計能力的提升以及面對物件程式設計的重要性。每次作業的題目數量雖然在減少,但難度卻逐漸加大,這讓我體會到了程式設計學習的漸進性和挑戰性。在前幾道題中,我們可以透過簡單地建立幾個類迅速完成,但最後一題往往需要我們綜合運用所學知識,真正考驗我們的程式碼能力。隨著作業的深入,程式碼的複雜性和功能需求也在逐漸增加。這種變化促使我在程式設計時必須更加註重類的設計和功能劃分,合理的類結構不僅可以提高程式碼的可讀性和可維護性,還能實現程式碼的複用,這正是面對物件程式設計的核心優勢。透過這三次作業,我不僅提高了自己的程式設計能力,更加深刻地理解了面對物件程式設計的價值。同時,龐大的程式碼量和複雜的輸入形式也促使我在思維方式上更加靈活,能夠更好地應對問題。然而,我也意識到自己在正規表示式的應用上還有很大的進步空間。儘管正規表示式極為便利且功能豐富,但在實際使用中,我發現自己對其掌握並不夠充分。在編寫正規表示式時,我經常需要反覆修改,導致時間的浪費。這讓我明白,只有透過深入學習和實踐,才能真正掌握這一強大的工具。因此,在接下來的學習中,我將更加重視正規表示式的研究,努力提高自己的能力。對於教師、課程和作業的改進建議,我認為可以增加一些針對正規表示式的專項練習,以幫助學生更好地理解和運用。此外,在課程組織上,可以引入更多的實踐案例,讓學生在解決實際問題中提升程式設計技能。透過這次階段性的總結,我對未來的學習方向有了更清晰的認識,希望在今後的學習中能不斷提升自己的程式設計能力,掌握更多實用的知識與技能。