第四-六次作業總結

黄文慧發表於2024-11-23

一、前言

該系列題目均為設計與實現一個小型的答題判題程式,題目難度從簡單到複雜逐步提升,題目數量和輸入資訊也逐步增加。以下是對這三道題的總結:

1. 知識點

  • 第4次題目集在第3次題目集的基礎上又增加了難度,該題目集主要考察程式設計技巧和演算法設計,特別是在答題系統中如何透過輸入的資料來判斷是否符合給定的標準和規則。判題邏輯的實現,包括如何解析輸入資料、處理各種異常情況,並進行答題結果的判斷。提高了程式碼的效率和穩定性,要求更高的程式設計能力。

  • 第5次題目集主要涉及電路設計與模擬。
    重點考察:
    開關控制:如何透過開關來控制裝置的開閉狀態。
    電路連線:包括串聯與並聯電路的基本理解。
    裝置狀態:根據輸入電壓來確定裝置(如燈、風扇等)的狀態或引數。
    基本的電路元件模型,包括開關、風扇、燈具等。

  • 第6次題目集:相比“7-1 家居強電電路模擬程式-1”,這一題目集在原有基礎上增加了更加複雜的功能,涉及更多的裝置和更復雜的電路配置。
    具體知識點包括:
    分檔調速器和連續調速器:如何根據輸入調節裝置的狀態,尤其是調速器的檔位調整。
    並聯電路的引入:如何處理並聯電路中的裝置控制和電流分配。
    對電路裝置的狀態管理更加精細,裝置的輸入輸出變化要求更加精確。
    物理電路計算,包括電壓、電流、電阻等方面的知識。

2. 題量

  • 題目4:題量較多,問題的難度更多在於邏輯的判斷和多種輸入的處理。
  • 題目5:題量相對較少,主要是一些基礎裝置的控制和簡單電路的連線問題。
  • 題目6:增加了更多的裝置種類和複雜的電路設計,題量有所增加。

3. 難度

  • 題目4:最具挑戰性,特別是在輸入資料的解析和判題邏輯的設計上,考察的重點是演算法和程式設計技巧。
  • 題目5:最為簡單,重點考察基礎電路原理和基本程式設計能力。
  • 題目6:難度適中,加入了調速器和並聯電路等複雜電路的設計,要求較強的電路分析能力。

二、設計與分析

第四題

該程式實現了一套完整的考試系統,從題庫管理、試卷設計、學生資訊錄入,到答題處理和成績計算,具備較強的實際操作價值。以下是對原始碼的詳細分析,包括類設計、功能實現、資料流向等,同時配有類圖與分析心得。

1. 類設計

  • wt(抽象類):題目基類,定義了抽象方法 cj(評分) 和 sc(結果輸出)。
  • xzt (單選題):實現評分與結果輸出邏輯,依據答案完全匹配或部分匹配給分。
  • dx (多選題):實現多選題評分,支援全對、部分對、全錯的情況。
  • sj (試卷類):維護試卷中的題目及分值分佈,確保總分為 100。
  • Student類:儲存學生資訊(學號、姓名)。
  • da (答題卡):儲存學生在試卷上的答題記錄。
  • Main類:主程式,處理輸入並呼叫其他模組完成題庫管理、答題解析與成績輸出。

2. 主程式邏輯分析

  • 題庫管理:使用正規表示式解析輸入資料 (#N 單選題, #Z 多選題)。questions 儲存所有題目物件。

  • 試卷設計:解析 #T 輸入,分配題目與分值。檢查試卷總分是否為 100 分。

  • 答題管理:使用正則匹配解析學生答案 (#S)。儲存在 answerSheets 中。

  • 評分流程:遍歷 answerSheets 中的每個學生答卷。對每題評分,累計總分,並輸出詳細結果。

  • 核心程式碼:

     if (testPaper == null) { // 校驗試卷是否存在
         System.out.println("alert: test paper " + sheet.hao + " not found");
         return;
     }
    
     double zf = 0; // 總分
     List<String> results = new ArrayList<>();
    
     for (Map.Entry<Integer, String> entry : sheet.getAnswers().entrySet()) {
         int qId = entry.getKey(); // 當前題目ID
         wt question = questions.get(qId); // 獲取題目物件
         if (question == null || !testPaper.getQuestions().contains(qId)) {
             System.out.println("the question " + qId + " invalid~0");
             results.add("0");
             continue;
         }
    
         // 評分
         double f = question.cj(entry.getValue()) * testPaper.getScore(qId);
         zf += f; // 累加分數
         System.out.println(question.sc(entry.getValue(), f)); // 輸出答案評價
         results.add(String.valueOf((int) f));
     }
    
     // 輸出學生成績
     System.out.println(student.id + " " + student.mz + ": " + String.join(" ", results) + "~" + (int) zf);
    
    
    
    

3. 設計類圖(使用PowerDesigner)

報表

指標
總行數(LOC) 211行
類數量 7
方法數量 19
最大圈複雜度 7
平均每行程式碼的字母數 41

4. 心得總結

  • 物件導向原則實踐:抽象基類(wt)+多型(xzt & dx)是符合開閉原則的設計,便於後續新增題型。
  • 資料流完整性:透過多級對映(questions、testPapers、answerSheets),維護各模組資料之間的關係。
  • 輸入解析設計:藉助正規表示式解決複雜資料的處理,極大提高了程式碼的通用性與靈活性。

第五題

程式碼實現的核心目標是模擬多個型別的電氣裝置(如開關、調速器、燈泡、吊扇等),透過命令對裝置進行控制並模擬其響應(電壓、電流、亮度、速度等)。

1. 類分析

  • Device 類
    定義了裝置的通用屬性(如 id、type、pin1、pin2 等)和行為(如 getOutputVoltage())。
    為後續子類提供了統一介面。

  • ControlDevice 和 ControlledDevice
    ControlDevice:用於實現帶輸入控制功能的裝置(如開關和調速器)。
    ControlledDevice:用於模擬電壓差和其對裝置的影響(如燈泡亮度、吊扇速度)。

  • SwitchDevice、MultiSpeedController、ContinuousController
    實現了具體的裝置邏輯(如開關的開閉狀態、調速器的檔位變化等)。

  • WhiteBulb、DaylightBulb 和 Fan
    根據電壓差計算裝置引數(亮度或轉速)。
    例如白熾燈的亮度是電壓差的線性函式,而吊扇的轉速與電壓差之間有複雜的關係。

  • UnionFind 類
    並查集實現,管理引腳之間的連線關係。
    透過路徑壓縮和合並最佳化,支援高效的連線查詢操作。

  • Main類
    主類,處理使用者輸入、解析命令、初始化裝置和計算電壓等核心邏輯。

2. 類圖設計(使用PowerDesigner)

使用PowerDesigner生成的類圖如下:

報表

以下是針對該程式碼的 SourceMonitor 報表分析:

指標
總行數(LOC) 626 行
方法數量 25
類的數量 8
平均方法複雜度 8
最大方法複雜度 8

順序圖

3. 心得總結

這個系統實現了電氣裝置的基本控制和模擬,採用了並查集管理引腳連線,使用BFS傳播電壓,並根據電壓差來控制裝置的狀態。透過物件導向的設計和繼承,使得系統的擴充套件性和可維護性得到了保證。

第六題

實現了一個基於裝置控制的模擬系統,核心思想是透過並查集管理裝置間的引腳連線,模擬裝置之間的電壓變化,並透過輸入的控制命令來調整裝置的狀態。程式碼分為幾個主要部分:裝置管理、並查集的實現、電壓計算和裝置控制。

1.類設計

  • SwitchDevice (K)
    職責:表示開關裝置,能夠在開啟和關閉之間切換。
    方法:toggle() 切換開關的狀態,getOutputVoltage() 根據開關的狀態計算電壓輸出。
    設計思路:繼承自 ControlDevice,實現了開關特有的行為。
  • MultiSpeedController (F)
    職責:表示分檔調速器裝置,可以在不同檔位之間切換。
    方法:increaseGear() 和 decreaseGear() 用於改變檔位,getOutputVoltage() 根據當前檔位計算輸出電壓。
    設計思路:繼承自 ControlDevice,實現了分檔調速器的行為。
  • ContinuousController (L)
    職責:表示連續調速器,能夠透過設定一個連續的引數來控制輸出電壓。
    方法:setParameter() 用於設定檔位引數,getOutputVoltage() 根據當前檔位引數計算輸出電壓。
    設計思路:繼承自 ControlDevice,實現了連續調速的行為。
  • WhiteBulb (B) 和 DaylightBulb (R)
    職責:這些類表示兩種型別的燈泡,分別是白熾燈和日光燈。它們根據電壓差計算亮度。
    方法:computeBrightness() 計算亮度,getStatus() 返回亮度值。
    設計思路:繼承自 ControlledDevice,實現了根據電壓差計算亮度的行為。
  • Fan (D)
    職責:表示風扇,根據電壓差計算轉速。
    方法:computeSpeed() 計算轉速,getStatus() 返回轉速。
    設計思路:繼承自 ControlledDevice,實現了根據電壓差計算風扇轉速的行為。

2. 類圖設計(PowerDesigner)

以下是基於當前程式碼的類圖,展示了各個類的屬性和方法。

類圖說明

  • Device 類:是所有裝置類的基類,包含所有裝置共享的屬性和方法。
  • ControlDevice 類 和 ControlledDevice 類:分別為控制裝置和受控裝置的基類。控制裝置如 SwitchDevice、- MultiSpeedController、ContinuousController 等,受控裝置如 WhiteBulb、Fan 等,都繼承了相應的基類。
  • 並查集類 UnionFind:用於管理引腳的連線,提供了 find() 和 union() 方法,確保電壓差的計算正確。

SourceMonitor 報表分析

以下是針對該程式碼的 SourceMonitor 報表分析示例:

指標
總行數(LOC) 627行
方法數量 34
類的數量 11
平均複雜性 較高
註釋比例 15%

3. 心得

這個程式要完全實現對於我而言比較困難,雖然類圖做出來了,有了基本框架,但是在實現整個功能的時候,還是會遇到很多問題,在上一次程式碼的基礎上增加了部分功能,但是複雜度也提升了很多。

三、採坑心得

第四次作業

  • 輸入格式問題:
    在TEST_PAPER_PATTERN和ANSWER_PATTERN的匹配中,輸入資料需要嚴格按照指定格式給出,尤其是空格、分隔符等,稍有不匹配就會導致無法正確解析。確保輸入的每行資料符合預期格式非常重要。
  • 部分匹配邏輯的處理:
    對於單選題和多選題的部分正確情況,程式碼透過判斷contains來處理,但實際使用中可能會遇到學生答案與正確答案部分重合的情況,需要保證cj()方法的實現能夠靈活處理這種情況。
    例如,在dx類中,cj()方法透過集合比較是否包含所有答案,當前實現未考慮到答案順序問題。可能需要考慮題目答案的順序對評分的影響。
  • 得分計算:
    sj類中對於總分是否等於100的檢查是一個重要的業務規則,但在輸入格式錯誤或資料不完整時,這個校驗可能被忽略。確保資料完整性和準確性非常關鍵,若有缺失或者錯誤資料應進行及時提醒。
    在sc()方法中,輸出是以~符號連線每個結果,可能在格式化上需要進一步最佳化以符合輸出需求。

第五次作業

  • 並查集實現問題:
    在處理引腳之間的連線時,我使用了並查集(Union-Find)來管理引腳間的連通性。在合併引腳時,發現一些連線沒有被正確地識別出來,導致計算電壓差時引發異常或不準確的結果。
    透過仔細檢查並查集的find和union操作,確保每次都對輸入的引腳進行有效的查詢和合並。並且在find方法中增加了路徑壓縮以提高效率,確保每個引腳都能夠正確地找到其連線的“根節點”。
  • 電壓計算問題:
    在計算電壓時,由於裝置間的複雜連線關係,電壓差計算中出現了不一致的情況,尤其是在裝置之間的傳輸電壓沒有正確傳播。
    透過引入一個佇列來進行廣度優先搜尋(BFS),確保從VCC和GND出發的電壓能夠正確地傳播到每個組,並且每個裝置的電壓計算都遵循正確的邏輯。確保每個組的電壓都被更新,直到沒有新組需要更新。
  • 裝置控制問題:
    在處理控制命令時,發現裝置的狀態控制沒有及時更新,特別是在多速控制器(MultiSpeedController)和連續調速器(ContinuousController)上,電壓變化時這些裝置的狀態沒有及時反映。
    確保每個裝置的狀態(如檔位、亮度、速度等)在控制命令執行後能夠及時更新,並且在輸出時能夠根據裝置的當前狀態生成正確的結果。特別是在解析命令時,確保能夠正確識別每個裝置的操作型別。

第六次作業

  • 裝置控制邏輯的複雜性:
    在設計裝置控制邏輯時,尤其是在解析控制命令時,處理不同型別裝置的命令方式變得複雜。每個裝置都有不同的狀態和行為,這使得控制邏輯的設計變得冗長。
    對於不同型別的裝置,我們可以透過繼承和多型來統一管理裝置的控制方式。例如,開關裝置(SwitchDevice)、調速器(MultiSpeedController)和其他受控裝置(如白熾燈、吊扇等)都可以從統一的 ControlDevice 類繼承。
  • 裝置狀態的展示:
    對於每個裝置的狀態(如亮度、轉速等),根據電壓差來計算相應的狀態。然而,在初期版本中,某些受控裝置的狀態可能計算錯誤,導致輸出結果不準確。
    在裝置的狀態計算中,特別是對於亮度、轉速等受電壓差影響的裝置,確保每個裝置的狀態計算是基於其引腳電壓差的。
  • 電壓計算與BFS
    電壓計算的部分需要透過廣度優先搜尋(BFS)來遍歷所有組,並逐步計算每組的電壓值。最初的實現中,電壓的傳播過程並不完全正確,可能出現電壓計算不一致或遺漏的情況。
    在 BFS 中,每當找到一個電壓源(例如 VCC 或 GND),就從該源開始傳遞電壓。如果當前組的電壓已經計算出來,則跳過,否則繼續計算。

四、改進建議

第四次作業

  • 增強異常處理:學生提交了不存在的試卷,或答題卡格式錯誤時應詳細報錯。
  • 評分邏輯最佳化:多選題中可引入更細化的部分評分規則(如權重分配)。
  • 提高程式碼複用性:可增加 AnswerChecker 類來專門處理答案判定和計分邏輯。
  • 程式碼複用性:sc 方法中字串拼接邏輯重複,可提取為通用工具方法。

第五次作業

  • 複雜度與函式長度:Main 類中的核心函式(如 main())複雜度較高,長度超過 200 行。
  • 重複程式碼:部分程式碼邏輯(如裝置狀態更新)在多個裝置中實現,但存在部分重複。
  • 類的單一職責:Main 類職責較多,既處理輸入,又控制流程,還管理裝置例項,可以將輸入處理與裝置管理邏輯分離。
  • 異常處理:當前程式碼缺乏對異常的處理(如輸入格式錯誤、電壓為空時的特殊情況),應新增健壯性邏輯。

第六次作業

  • 程式碼結構:主函式 Main 中邏輯較為複雜,可將不同功能(如連線解析、裝置控制命令處理、電壓計算等)拆分到獨立方法中。
  • 註釋改進:增加複雜邏輯段的註釋,例如 BFS 計算電壓的細節、裝置狀態更新的條件判斷。
  • 降低複雜性:引入設計模式(如策略模式)最佳化裝置的狀態更新和電壓計算邏輯。

五、總結

這些題目涉及了物件導向程式設計(OOP)的多個方面,如類的設計、繼承、多型、抽象類、介面、集合框架的使用、正規表示式的處理、以及輸入輸出流的操作等。經過三次題目的練習,積累了以下幾點學習經驗和對未來改進的建議:

  • 繼承與多型

    透過繼承和方法重寫,實現了程式碼的複用和擴充套件。

  • 正規表示式

    掌握瞭如何使用正規表示式來解析和驗證字串。

  • 異常處理

    雖然在這些題目中沒有明確提到,但在實際程式設計中,異常處理是保證程式健壯性的重要手段。

  • 併發程式設計

    在處理多執行緒或併發問題時,需要更深入地學習Java的併發機制。

  • 設計模式

    為了提高程式碼的可維護性和可擴充套件性,需要學習設計模式。

對教師、課程和作業組織的建議

  • 教師建議:題目設計過程循序漸進,對學生理解複雜系統設計和多資訊處理有較好幫助,但因複雜度較高,建議在課上增加難點講解,尤其是資訊引用管理和異常處理方面的案例分析。