題目集4-6總結

李泽月發表於2024-11-23

前言

  1. 三次題目集的知識點、題量、難度
    (1).題目集4知識點
    題目集4為第一次大作業最後一次迭代,以下為知識點整理:
    1. 物件導向程式設計:
      繼承 (Inheritance):SingleChoiceQuestion、FillInBlankQuestion 和 MultipleChoiceQuestion 都繼承了 Question 類。這使得它們能夠共享公共欄位和方法,同時又能根據不同型別的題目重寫 grade() 和 getOutput() 方法。
      多型 (Polymorphism):透過父類 Question 型別的引用,可以指向不同子類的物件,例如,Question question 變數可以引用 SingleChoiceQuestion、FillInBlankQuestion 或 MultipleChoiceQuestion 物件。在呼叫 grade() 或 getOutput() 時,Java 會根據實際物件的型別執行相應的子類實現(重寫方法)。
      封裝 (Encapsulation):類中的屬性透過 protected 或 private 訪問修飾符封裝,只能透過建構函式或方法進行訪問。
    1. 集合類 (Collections):
      List:List 用來儲存每個學生的答題詳情,例如 detailedResults 用來儲存每道題目的得分。
      Map:程式碼中大量使用了 Map 來儲存資料,如 Map<Integer, Question> 儲存題目,Map<String, TestPaper> 儲存試卷,Map<String, AnswerSheet> 儲存學生的答案等。Map 允許透過鍵查詢相應的值。
      Set:Set 用來儲存多選題的正確答案和學生的選擇答案。Set 具有唯一性,因此可以避免重複選擇。
    1. 正規表示式 (Regex):
      Pattern 和 Matcher:在處理輸入時,使用了正規表示式來解析不同格式的題目、試卷、學生資訊和答案。例如,QUESTION_PATTERN 用於匹配單選題的輸入,ANSWER_PATTERN 用於匹配學生的答案輸入。這使得輸入格式的解析更為靈活和簡潔。
    1. 流式操作 (Streams):

      Stream API:在多選題中,透過 Stream 操作過濾出不正確的答案:
      點選檢視程式碼
      Set<String> incorrectAnswers = studentAnswers.stream()
             .filter(answer -> !correctAnswers.contains(answer))
             .collect(Collectors.toSet());
      
      此程式碼利用了 filter() 來篩選出學生答案中不在正確答案中的元素,然後透過 collect() 將結果收集到 Set 中。
    1. 排序 (Sorting):
      使用了 List 和 Map 的排序功能。對於學生的答案進行按試卷 ID 排序:

      點選檢視程式碼
      matchedAnswerSheets.sort((entryA, entryB) -> {
      int testPaperIdA = Integer.parseInt(entryA.getKey().split("-")[1]);
      int testPaperIdB = Integer.parseInt(entryB.getKey().split("-")[1]);
      return Integer.compare(testPaperIdA, testPaperIdB);
      });
      
      這裡透過 Integer.compare() 按照試卷 ID 從小到大進行排序。
    1. 異常處理與邊界檢查:
      在處理學生答案和試卷時,進行了有效性檢查,確保資料的完整性。例如:

      點選檢視程式碼
      if (!testPaper.hasQuestion(questionOrder) || deletedQuestions.contains(questionOrder)) {
      System.out.println("the question " + questionOrder + " invalid~0");
      detailedResults.add("0");  // 無效題目得分為0
      continue;
      }
      
      這確保瞭如果題目無效或者已刪除,不會影響評分。
    1. 資料結構和演算法:
      LinkedHashMap:在 TestPaper、AnswerSheet 類中使用了 LinkedHashMap 來保持插入順序,確保題目順序和學生的答題順序保持一致。
      HashSet:在多選題評分中,使用了 HashSet 來儲存答案,這樣可以避免重複答案的干擾。
    1. 類設計與職責分離:
      單一職責原則 (Single Responsibility Principle):每個類有明確的職責。例如,SingleChoiceQuestion、FillInBlankQuestion 和 MultipleChoiceQuestion 類只負責題目的評分和反饋輸出;TestPaper 只負責試卷和題目的關係;AnswerSheet 只儲存學生的答案;Student 儲存學生的基本資訊。
      程式碼模組化:Main 類負責驅動整個程式的流程,讀取輸入,解析並呼叫其他類的功能,確保程式碼模組化,職責清晰。
    1. 控制結構與迴圈:
      while迴圈:用於讀取使用者輸入直到遇到“end”命令。
      for-each 迴圈:用於遍歷學生、試卷和答案。
    1. 字串操作:
      在多個地方使用了 split()、trim() 和 contains() 等方法處理字串。這些方法非常常見,用於解析和驗證輸入內容。
      總結來說,這段程式碼展示了Java中的物件導向程式設計(繼承、多型)、集合框架的使用(List, Map, Set)、正規表示式處理輸入、流式操作、排序、異常處理和設計模式等多個常見和高階的Java技術。
      此題難度
    1. 基礎知識點(適中)
      類和物件:程式碼中使用了多個類(SingleChoiceQuestion、FillInBlankQuestion、MultipleChoiceQuestion、TestPaper、Student、AnswerSheet),每個類的職責清晰。對於初學者來說,理解如何透過類和物件來組織程式是一個基礎但關鍵的概念。
      建構函式:每個類的建構函式明確接受並初始化資料,這個部分的理解相對簡單。
      條件判斷和迴圈:使用了多次條件判斷(if)和迴圈(while、for-each),這些是Java基礎中必不可少的知識。
    1. 中級知識點(有一定挑戰性)
      繼承和多型:使用了物件導向的繼承和多型機制。SingleChoiceQuestion、FillInBlankQuestion 和 MultipleChoiceQuestion 類都繼承自 Question 類,並重寫了 grade() 和 getOutput() 方法。理解這種設計模式對於初學者來說是一個挑戰,尤其是如何利用多型來處理不同型別的問題。
      集合類的使用:List、Set 和 Map 的使用比較廣泛,特別是在處理學生答案和試卷資訊時。理解這些集合類的特性(例如 HashSet 的去重功能、LinkedHashMap 保持順序等)對於初學者來說可能需要一定的時間。
      正規表示式:程式碼中廣泛使用了正規表示式來解析輸入資料。理解正規表示式的語法和如何在程式碼中使用它們可能對於初學者具有一定挑戰性。
    1. 高階知識點(需要一定經驗)
      流式程式設計(Streams):程式碼中使用了 Java 8 的 Stream API 來進行集合的過濾和處理,例如:
      Set incorrectAnswers = studentAnswers.stream()
      .filter(answer -> !correctAnswers.contains(answer))
      .collect(Collectors.toSet());
      雖然流式操作使得程式碼更加簡潔和高效,但它需要一定的程式設計經驗來理解和使用。
      排序:程式碼中使用了自定義的排序邏輯來對 AnswerSheet 進行排序:
      matchedAnswerSheets.sort((entryA, entryB) -> {
      int testPaperIdA = Integer.parseInt(entryA.getKey().split("-")[1]);
      int testPaperIdB = Integer.parseInt(entryB.getKey().split("-")[1]);
      return Integer.compare(testPaperIdA, testPaperIdB);
      });
      這種方式在處理複雜排序時非常實用,但需要對 Java 排序機制有一定了解。
    1. 邊界條件與異常處理(適中)
      資料驗證:程式碼中對各種輸入資料進行了驗證和處理,如檢查題目是否存在、是否有效、是否被刪除等。這是一個非常好的設計,能夠保證程式的健壯性。
      處理無效題目和答案:當學生的答案與試卷題目不匹配時,程式會進行錯誤處理並輸出相應資訊。理解如何處理無效資料並反饋資訊是程式設計中的常見問題。

    (2)題目集5知識點

    1. 物件導向程式設計 (OOP)
      抽象類:程式碼中使用了abstract關鍵字定義了抽象類,如Device、ControlDevice、ControlledDevice等。抽象類不能直接例項化,只能作為其他類的基類。它允許定義一些共享的方法和強制派生類實現特定的抽象方法。

      abstract class Device { ... }
      updateOutputVoltage() 和 getOutput() 都是抽象方法,要求子類必須實現這些方法。
      繼承:子類繼承了父類的屬性和方法,可以擴充套件或重寫父類的方法。
      class SwitchDevice extends ControlDevice { ... }
      class MultiLevelDimmer extends ControlDevice { ... }
      例如,SwitchDevice類繼承了ControlDevice類,ControlDevice又繼承自Device類。繼承可以實現程式碼重用和擴充套件。
      多型:透過多型可以在父類引用中操作子類物件。程式碼中Device型別的引用指向不同型別的子類物件(如SwitchDevice, MultiLevelDimmer等),並透過該引用呼叫子類的updateOutputVoltage()等方法。
      Device device = new SwitchDevice("K1");
      device.updateOutputVoltage();

    1. 抽象方法和實現方法
      子類必須實現父類中的抽象方法。例如,SwitchDevice實現了updateOutputVoltage()和getOutput()方法:
      @Override
      void updateOutputVoltage() { ... }
      @Override
      String getOutput()
    1. 介面的隱式使用
      在程式碼中,沒有顯式使用介面,但可以觀察到使用了抽象類來實現類似介面的功能。例如,ControlDevice和ControlledDevice類分別為不同型別裝置的基類,提供了共通的介面(如updateOutputVoltage()等)。
    1. 集合類
      List:用來儲存裝置的連線資訊和命令。常見的集合類如ArrayList用於儲存不重複的資料項。
      List connections = new ArrayList<>();
      List commands = new ArrayList<>();
      Map:Map用於將裝置ID對映到裝置物件。HashMap是用於儲存鍵值對的集合。
      Map<String, Device> deviceMap = new HashMap<>();
      Set:用來儲存唯一的裝置ID,防止重複操作。
      Set visited = new HashSet<>();
    1. 條件判斷與方法呼叫
      條件判斷:if語句被廣泛用於控制裝置的開關、檔位或引數設定。
      if (level < 3) level++;
      if (inputVoltage < 80.0)
    1. 字串處理
      使用了 substring()、split() 等方法處理字串。例如,從裝置編號中提取數字、解析連線資訊等。
      String deviceId = pin.split("-")[0];
    1. 排序
      在處理裝置時,程式碼透過自定義比較器對裝置進行排序,先按裝置型別(K, F, L, B, D, R)排序,再按裝置編號排序:
      sortedDevices.sort(new Comparator() { ... });
    1. 棧和深度優先搜尋 (DFS)
      使用棧實現深度優先搜尋(DFS),從VCC裝置開始處理,確保下游裝置的輸出電壓計算優先進行。
      Stack stack = new Stack<>();
      stack.push("VCC");
      while (!stack.isEmpty())
    1. 遞迴/迭代與迴圈
      在電壓更新過程中,使用了迭代方式更新裝置的輸出電壓,直到電壓穩定為止。每次迭代透過檢查電壓變化來決定是否繼續。
      boolean changed = true;
      while (changed)
    1. 格式化輸出
      使用String.format()格式化輸出,確保輸出電壓等資料的格式符合要求。
      return String.format("%.2f", parameter);
    1. 匿名類
      在程式碼中使用了匿名類來實現自定義的Comparator,對裝置進行排序。
      sortedDevices.sort(new Comparator() {
      @Override
      public int compare(Device d1, Device d2) { ... }
      });
    1. 異常處理
      程式碼沒有顯式地使用異常處理(try-catch),但在實際應用中,涉及到輸入輸出、數值轉換等操作時,通常需要考慮異常處理來確保程式的健壯性。
    1. 輸入輸出
      使用Scanner類讀取輸入,System.out.println()輸出結果:
      Scanner sc = new Scanner(System.in);
      while (sc.hasNextLine()) { ... }
      System.out.println("@" + device.identifier + ":" + device.getOutput());
    1. 內部類
      Device類的子類VCC和GND被定義為匿名內部類,用於模擬VCC和GND裝置的行為。這些裝置是特殊的,它們的輸出電壓始終為220V和0V。
      此題難度
      難度較高,主要體現在以下幾個方面:
    1. 物件導向設計
      抽象類與繼承:程式碼中使用了大量的抽象類和繼承,表現出較高的物件導向設計技巧。Device 類是所有裝置的父類,具體裝置(如 SwitchDevice、IncandescentLamp 等)透過繼承 Device 類來實現特定的功能。
      多型與方法重寫:不同裝置型別透過方法重寫(如 updateOutputVoltage 和 getOutput)來實現不同的行為,這展示了較強的多型使用技巧。
    1. 裝置型別的複雜性
      程式碼處理了多種裝置型別(開關、調光器、燈具、電扇等),每種裝置具有不同的輸入輸出邏輯。例如,IncandescentLamp 根據電壓變化調節亮度,Fan 根據電壓變化調節轉速。要理解這些裝置型別如何互動,需要對電氣裝置和程式設計有一定了解。
    1. 電路模型的建立與計算
      電路連線與資訊流動:程式碼模擬了一個電路的狀態,其中每個裝置的輸出依賴於其輸入,而輸入又來自上游裝置。這需要正確地建立裝置間的連線關係,並按照適當的順序計算裝置的狀態(即從 VCC 到 GND 的電壓傳遞)。
      反覆迭代更新電壓:電壓需要反覆計算,直到系統穩定。這個過程類似於物理模擬或電路模擬,涉及到每個裝置根據電壓更新輸出狀態,且裝置間的狀態更新是相互依賴的。
    1. 命令處理
      輸入命令的解析與處理:程式碼中包含了使用者輸入的命令解析部分,透過命令來控制不同裝置(如切換開關、調節調光器等)。這一部分需要設計良好的命令解析邏輯,並根據命令的不同型別(開關、調光器等)來呼叫相應的方法。
    1. 資料結構使用
      對映與集合:程式碼使用了多種資料結構,如 Map<String, Device> 來管理裝置,List<String[]> 儲存電路連線關係,Set 來避免重複計算,Stack 實現深度優先搜尋等。這些資料結構的使用表明程式碼涉及到較複雜的資料組織和流程控制。
      裝置排序:最終輸出裝置的順序需要根據裝置型別(K, F, L, B, D, R)和裝置編號進行排序,這要求開發者熟悉如何自定義比較器來實現排序。
    1. 模擬與計算精度
      程式碼需要不斷地類比電路的動態變化,直到系統穩定。計算中涉及到電壓的精度比較(Math.abs(device.outputVoltage - oldOutput) > 1e-6),這需要對浮點數精度控制有一定的瞭解。

(3)題目集6知識點

  • 1.類與物件(Class & Object):
    定義了多個類,如Device(裝置基類)、SwitchDevice(開關裝置)、StepController(分檔調速器)、IncandescentLamp(白熾燈)等,這些類透過建構函式、方法和繼承進行擴充套件和使用。
    各裝置類的屬性、方法和行為被封裝在類內部,類之間透過繼承關係實現程式碼重用和多型性。

  • 繼承(Inheritance):
    SwitchDevice、StepController、IncandescentLamp等類繼承自Device類,並實現了computeOutput()和getOutput()等抽象方法。繼承使得不同裝置共享共同的行為,如computeOutput方法的實現方式。

  • 抽象類和方法(Abstract Class & Method):
    Device類是一個抽象類,其中定義了抽象方法computeOutput()和getOutput(),這些方法由具體裝置類來實現。抽象類無法例項化,只能透過繼承和實現抽象方法來使用。

  • 介面和多型(Interface & Polymorphism):
    透過繼承不同的裝置類,每種裝置物件具有不同的computeOutput()實現。裝置類透過重寫computeOutput和getOutput實現不同裝置的獨特行為。

  • 集合框架(Collections Framework):
    使用了List、Map等集合型別來儲存和管理資料。例如,devices儲存所有裝置,seriesMap和parallelMap儲存串聯電路和並聯電路的資訊,commands儲存使用者輸入的控制命令。

  • 條件語句與迴圈(Conditionals & Loops):
    在多個地方使用了if條件語句來判斷裝置型別、控制命令等,確保裝置狀態的變化符合預期。
    使用for迴圈遍歷裝置和電路資訊,實現裝置的建立和命令的處理。

  • 字串操作(String Manipulation):
    使用字串的split()方法來分割輸入命令、裝置標識等資訊。透過substring()方法提取子字串,利用正規表示式對字串進行匹配和處理。

  • 輸入輸出(I/O Operations):
    透過Scanner類讀取使用者輸入的每一行,解析並儲存電路和裝置配置。輸入被逐行讀取並儲存到inputLines中。

  • 錯誤處理(Error Handling):
    在部分輸入命令中,如果格式不正確(如缺少必要的部分),程式碼透過continue跳過無效行。
    使用try-catch捕獲轉換引數時可能發生的錯誤,如解析浮動引數時的NumberFormatException。

  • 遞迴(Recursion):
    computeVoltage()方法是遞迴的,它透過傳入的電路資訊遞迴地計算電壓分配。在並聯電路中,遞迴計算每個串聯電路的電壓,並找到最大電壓作為輸出。

  • 資料結構(Data Structures):
    使用HashMap來儲存裝置、串聯電路和並聯電路,透過裝置ID或電路ID快速查詢。
    使用ArrayList來儲存裝置和電路連線,便於動態新增、移除元素。
    格式化輸出(Formatted Output):
    在輸出裝置的狀態時,使用String.format()對浮動引數進行格式化,確保輸出符合指定的小數位數。
    此題難度
    難度較高,涉及多個高階程式設計概念和複雜的邏輯,具體來說,難度主要體現在以下幾個方面:

    1. 物件導向程式設計(OOP)的理解與應用
      抽象類與繼承:程式碼中涉及多個抽象類和繼承關係,如Device類及其子類(如SwitchDevice、StepController等)。理解抽象類和繼承的機制,以及如何利用這些特性組織程式碼,是這段程式碼的一個核心難點。
      多型:透過子類重寫父類的方法來實現不同裝置型別的行為,這要求對多型的概念有較強的理解。
    1. 遞迴與複雜的電路計算
      computeVoltage()方法的遞迴計算較為複雜,特別是在電路分析時需要考慮並聯和串聯電路的不同處理方式。遞迴邏輯要求理解如何逐步分解問題,以及如何保證遞迴的終止條件正確。
    1. 資料結構的使用
      使用HashMap、ArrayList等資料結構來儲存和管理裝置、串聯電路、並聯電路等資訊,需要對資料結構有一定的掌握。
      例如,如何用Map快速查詢電路或裝置,如何組織和處理動態資料,特別是管理多個電路的連線關係,考驗程式設計者的設計能力。
    1. 輸入輸出與錯誤處理
      程式中需要從使用者輸入中解析資料並處理錯誤,如處理格式不正確的輸入、異常的輸入值等。這要求編寫者具備較強的輸入輸出處理能力,並能夠設計出合理的錯誤處理機制。
    1. 複雜的命令解析
      輸入命令的解析(如透過split()和substring()方法從字串中提取資訊)較為複雜,特別是要處理各種不同格式的命令。正確處理這些命令需要良好的字串操作能力。
    1. 電路與裝置模型的設計
      需要設計出一個能夠合理類比電路和裝置行為的模型,涉及到的物理學知識(如電壓、電流等的計算)可能對一些開發者來說是新的挑戰。
      尤其是如何在不同電路配置(並聯和串聯)下計算電壓和電流,涉及到一定的電路基礎和計算邏輯。

設計與分析及踩坑心得和改進建議

  1. 題目集4
  • 設計與分析

    1. 類的設計與職責
      Question 類
      Question 類是所有題目的基類,包含了題目的基本資訊(如正確答案、題目文字、是否多選、是否有效)。
      該類提供了預設實現的 grade() 和 getOutput() 方法,子類可以重寫這些方法以適應不同題型的評分與反饋。
      grade() 用來計算得分,預設返回 0.0。
      getOutput() 用來生成題目的反饋資訊,預設返回空字串。
      SingleChoiceQuestion 類(單選題)
      繼承自 Question 類,重寫了 grade() 和 getOutput() 方法。
      grade() 方法判斷使用者回答是否與正確答案完全一致,部分一致時給出部分分,其他情況不給分。
      getOutput() 方法根據答案的對錯,返回不同的反饋資訊。
      FillInBlankQuestion 類(填空題)
      同樣繼承自 Question 類,重寫了 grade() 和 getOutput() 方法。
      grade() 方法判斷使用者填寫的空白是否正確,並給出 0、0.5 或 1 的得分。
      getOutput() 方法根據答案對錯,生成反饋資訊。
      MultipleChoiceQuestion 類(多選題)
      繼承自 Question 類,重寫了 grade() 和 getOutput() 方法。
      grade() 方法考慮了多選題的特殊性,透過集合的比較來判斷是否完全正確、部分正確,或者全錯。
      getOutput() 方法生成適合多選題的反饋資訊。
      TestPaper 類(試卷)
      TestPaper 類管理一份試卷的基本資訊,包括試卷 ID 和包含的所有問題。
      使用 addQuestion() 方法將問題與分數新增到試卷中,getTotalScore() 用來計算試卷的總分。
      Student 類(學生)
      儲存學生的 ID 和姓名。
      AnswerSheet 類(答卷)
      儲存學生的答卷資訊,包括試卷 ID、學生 ID 以及每個問題的答案。
      使用 addAnswer() 方法將答案新增到答卷中。
    1. 核心流程分析
      輸入資料解析
      使用正規表示式來解析輸入資料。不同型別的資料透過不同的正則模式進行匹配,分別處理題目、試卷、學生資訊、答案和刪除題目等輸入。
      QUESTION_PATTERN、FILL_IN_BLANK_PATTERN 和 MULTIPLE_CHOICE_PATTERN 用於匹配不同型別的題目輸入,分別建立 SingleChoiceQuestion、FillInBlankQuestion 和 MultipleChoiceQuestion 物件。
      TEST_PAPER_PATTERN 處理試卷資訊,建立 TestPaper 物件,並透過 addQuestion() 新增題目。
      STUDENT_PATTERN 用於解析學生資訊。
      ANSWER_PATTERN 用於解析學生的答案。
      DELETE_PATTERN 用於標記刪除的題目。
      評分與反饋輸出
      處理答卷時,對於每個問題,首先判斷問題是否有效(未被刪除),然後根據題目的型別呼叫對應的 grade() 方法計算得分,並透過 getOutput() 輸出反饋資訊。
      評分時,每道題目的得分會乘以該題目的最大分數,從而得到最終的得分。
      多執行緒/併發
      目前的實現是序列的,即逐個學生逐個試卷地處理,沒有涉及併發處理。如果要處理大量資料,可能需要考慮多執行緒或併發技術。
    • 踩坑心得與改進建議
      錯誤處理
      輸入格式錯誤或資料缺失時沒有全面的錯誤提示。可以增加更多的錯誤處理邏輯,如輸入格式不符合預期時給出明確的提示。
      在處理題目時,如果題目無效(例如被刪除),應返回相關的錯誤提示,而不是單純的得分為 0。
      程式碼複用性
      SingleChoiceQuestion、FillInBlankQuestion 和 MultipleChoiceQuestion 的 grade() 和 getOutput() 方法有很多重複的程式碼(例如判斷題目是否有效的部分),可以考慮提取成一個共用的函式以減少重複。
      資料結構最佳化
      AnswerSheet 類中的 answers 使用了 LinkedHashMap,這樣可以保證輸入的順序。如果順序不重要,可以考慮使用 HashMap 來最佳化效能。
      TestPaper 類中的 questions 使用了 LinkedHashMap,若不需要保持順序,可以使用 HashMap,這樣在查詢時的效能會更好。
      刪除題目時的處理
      當前刪除題目時只是將題目標記為無效,但在 processAnswers 方法中,仍然會繼續處理這些題目。如果題目已刪除,應該跳過該題目的評分,或者在輸出中直接標明該題已刪除。
  1. 題目集5
  • 設計與分析

    1. 抽象裝置類 Device
      Device 是所有電氣裝置的基類,包含裝置的基本屬性和行為。
      屬性:
      identifier: 裝置的識別符號(如 "K1", "F2")。
      inputVoltage: 裝置的輸入電壓。
      outputVoltage: 裝置的輸出電壓。
      抽象方法:
      updateOutputVoltage(): 根據裝置的工作原理,更新輸出電壓。
      getOutput(): 返回裝置的輸出狀態(如開關狀態、亮度、速度等)。
      getType(): 返回裝置型別,用於分類排序。
      getNumber(): 獲取裝置編號,從裝置識別符號中解析(去掉字母部分)。
      設計意義: Device 類作為基類提供了裝置的通用屬性和方法,方便派生類繼承並實現具體裝置的行為。
    1. 控制裝置類 ControlDevice 和受控裝置類 ControlledDevice
      控制裝置類: ControlDevice 是所有控制裝置(如開關、調速器)的基類,繼承自 Device 類。
      受控裝置類: ControlledDevice 是所有受控裝置(如燈具、風扇)的基類,繼承自 Device 類。它包含了更新裝置狀態的 updateDeviceState() 方法。
      設計意義: 將控制裝置與受控裝置分開,體現了裝置間的功能區分。控制裝置直接影響電路的狀態,而受控裝置則根據輸入電壓執行特定任務(如調節亮度、速度)。
    1. 具體裝置類
      開關裝置 (SwitchDevice): 控制電路的開關狀態,透過 toggle() 方法切換狀態(開/關),根據狀態更新輸出電壓。
      分檔調速器 (MultiLevelDimmer): 透過不同的檔位(0-3)調節輸出電壓,影響輸出電壓的倍率(0, 0.3, 0.6, 0.9)。
      連續調速器 (ContinuousDimmer): 透過設定一個引數(0.00-1.00)來調節輸出電壓。
      白熾燈 (IncandescentLamp): 根據輸入電壓(10V-220V)調整亮度。亮度與輸入電壓成線性關係。
      日光燈 (DaylightLamp): 輸入電壓為非零時亮度固定為180。
      吊扇 (Fan): 根據輸入電壓調節風扇轉速,轉速與輸入電壓成線性關係。
      設計意義: 每個具體裝置類根據其特性實現了 updateOutputVoltage() 和 updateDeviceState() 方法,體現了多型的應用。每個裝置處理自身的輸出電壓和狀態。
    1. 裝置連線和電壓傳遞
      裝置之間透過 connectionPairs 陣列表示連線關係。
      在電路中,VCC 裝置的輸出電壓固定為 220V,而 GND 裝置的輸出電壓固定為 0V。
      每個裝置根據其上游裝置的輸出電壓更新自己的輸入電壓,之後計算新的輸出電壓。
      設計意義: 模擬了電路中電壓的傳遞過程,體現了裝置之間的依賴關係。透過圖示連線關係,保證了電壓的傳遞順序。
    1. 命令與狀態更新
      程式會讀取一系列命令(例如 #K1 切換開關狀態,#F1+ 調整調速器檔位)。
      根據命令更新相應裝置的狀態,呼叫 toggle()、increase()、decrease() 或 setParameter() 方法修改裝置行為。
      設計意義: 命令機制使得模擬系統能夠動態控制裝置的狀態,並即時反饋裝置的變化,增強了系統的互動性。
    1. 電壓更新與穩定
      電壓更新過程使用了一個迭代的方式,直到所有裝置的輸出電壓穩定為止。每次迭代時,裝置根據上游裝置的輸出電壓更新自己的輸入電壓,然後計算新的輸出電壓。
      穩定性檢測: 如果在某次迭代中,裝置的輸出電壓變化小於閾值(1e-6),就認為電壓穩定,退出迭代。
      設計意義: 電壓的迭代更新模擬了實際電路中的穩態過程,確保電路中所有裝置的電壓值最終達成一致。
    1. 裝置排序與輸出
      裝置在輸出時按型別(K, F, L, B, D, R)排序,確保輸出順序符合要求。
      在同型別裝置中,根據裝置編號進行排序。
      設計意義: 排序保證了輸出結果符合預期,且能夠清晰展示每個裝置的輸出狀態。
    1. 輸入與輸出
      輸入: 程式透過 Scanner 類讀取連線資訊和命令,解析連線關係並執行相應操作。
      輸出: 根據每個裝置的最終輸出電壓和狀態,按格式輸出裝置的狀態。
    1. 錯誤處理與魯棒性
      程式中沒有明確的錯誤處理機制,例如連線關係或命令格式不符合預期時的情況。可以進一步完善,比如透過正規表示式校驗輸入格式、裝置型別檢查等。
  • 踩坑心得及改進建議

    1. 裝置間的連線和電壓傳遞的實現
      問題:
      裝置的電壓傳遞和更新採用了迭代方式,但沒有明確處理裝置間的依賴關係。在實際電路中,電壓傳遞是一個依賴和影響的過程。如果有裝置同時連線多個輸入源,應該明確考慮其電壓的合併或優先順序。
      裝置之間的電壓傳遞存在效能問題:每次更新電壓時,都需要對所有裝置進行遍歷,直到所有裝置穩定。對於大型電路,可能導致效能瓶頸,尤其是在裝置數量龐大的情況下。
      最佳化建議:
      採用事件驅動或依賴關係圖(Dependency Graph)來管理裝置間的連線關係和電壓傳遞。透過這種方式,當一個裝置的狀態發生變化時,只通知相關的裝置進行更新,避免不必要的全遍歷。
      對於裝置的更新過程,可以引入一個優先順序佇列或拓撲排序來確保依賴關係正確處理,避免反覆無效的電壓計算。
    1. 電壓迭代的穩定性判斷
      問題:
      當前的穩定性判斷依賴於裝置輸出電壓的差值小於設定閾值(1e-6),但這一判斷方法對於某些特殊電路(例如高度非線性的裝置或有多個輸入源的複雜電路)可能不準確。
      沒有考慮裝置本身可能會發生“震盪”或“非穩態”的情況,某些裝置可能在多個輸入電壓條件下有複雜的行為模式。
      最佳化建議:
      使用更細緻的穩定性檢查機制。例如,可以透過限制最大迭代次數來避免死迴圈,或者採用更為複雜的收斂判斷條件。
      對於一些非線性或複雜裝置,考慮引入物理模型模擬其行為,或者透過更精確的數值解法(如牛頓法、迭代法)來計算電壓。
    1. 裝置型別和命令解析
      問題:
      程式透過字串解析裝置型別和命令,例如透過正規表示式來解析命令,但這可能會導致潛在的錯誤,特別是在命令輸入不規範的情況下。例如,裝置型別、編號、運算子之間的空格或符號錯誤可能導致解析失敗。
      對命令的解析沒有錯誤處理機制,若命令格式錯誤或者裝置不存在,程式可能會崩潰或返回不合預期的結果。
      最佳化建議:
      增加嚴格的輸入驗證和錯誤處理機制,確保命令格式的正確性。例如,可以透過正規表示式檢查輸入格式是否符合預期,並在解析過程中加入異常處理。
      在命令解析時增加反饋機制,若命令無效或裝置不存在,應向使用者返回明確的錯誤資訊,避免程式崩潰。
    1. 裝置排序和輸出
      問題:
      在裝置排序時,首先按型別排序,若同型別裝置較多,則按裝置編號排序。雖然這是一個合理的排序方式,但在複雜系統中,可能存在更為複雜的排序需求(例如按裝置功能或優先順序排序)。
      輸出時只考慮了電壓狀態和裝置型別,沒有顯示其他可能對使用者有用的資訊,如裝置的具體狀態、故障資訊等。
      最佳化建議:
      擴充套件裝置排序的規則,允許使用者自定義排序依據(如裝置優先順序或功能)。
      在輸出時提供更全面的資訊。例如,除了電壓和裝置型別,還可以顯示裝置的狀態、執行時間、故障資訊等,提供更具洞察力的系統反饋。
    1. 程式碼可擴充套件性和複用性
      問題:
      目前的程式碼在新增新裝置或修改現有裝置時可能需要大量修改現有類,尤其是在電壓計算或裝置行為方面,可能會影響多個類之間的關係。
      裝置之間的行為差異較大,但很多方法是透過繼承和重寫來實現的,這樣做可能會導致類的層次結構過於複雜,難以擴充套件。
      最佳化建議:
      引入介面和策略模式(Strategy Pattern)。透過定義統一的介面來規範裝置的行為,將裝置的具體實現和行為分離。這樣當需要新增新裝置時,只需要實現相應的介面,而不必修改現有程式碼。
      透過組合而不是繼承的方式來實現裝置間的複用。對於一些具有共享行為的裝置,可以考慮將其功能獨立出來,作為元件組合到具體裝置中。
    1. 裝置狀態更新與併發
      問題:
      當前的裝置狀態更新過程是順序執行的,如果電路中有多個裝置同時處於變化狀態,可能會導致更新過程過於緩慢或不可預期的行為。
      最佳化建議:
      引入多執行緒或非同步處理機制,尤其是在模擬較大電路時,可以考慮透過多執行緒並行更新裝置狀態。這樣可以有效提高程式的執行效率。
    1. 程式碼結構與模組化
      問題:
      當前程式碼雖然結構清晰,但所有裝置和電路的管理都集中在一個類中,隨著電路規模的擴大,可能導致類變得過於龐大和複雜,難以維護。
      最佳化建議:
      增強程式碼的模組化和解耦。可以將不同型別的裝置、命令解析、連線管理、電壓計算等功能拆分到不同的模組或類中,遵循單一職責原則,減少每個類的複雜度。

3.題目集6

  • 設計與分析

    1. 物件導向設計
      程式碼採用了物件導向的設計方法,透過抽象類、繼承和多型性組織和管理不同的裝置和電路型別。
      抽象類 Device:
      Device類是抽象類,其中定義了兩個抽象方法:computeOutput()和getOutput()。這些方法是裝置類的基礎功能,所有具體的裝置類(如SwitchDevice、StepController)必須實現這些方法。
      Device類為所有裝置類提供了統一的介面,促進了程式碼的擴充套件性和可維護性。每種裝置類可以有不同的computeOutput()實現,但它們都有相同的getOutput()方法。
      繼承與多型:
      透過繼承關係,SwitchDevice、StepController和IncandescentLamp等裝置類繼承了Device類,並實現了其抽象方法。多型性體現在同一方法呼叫時,可以根據裝置的實際型別(如SwitchDevice、IncandescentLamp)執行不同的行為。
      這使得程式碼在新增新的裝置型別時更為簡便,只需繼承Device並實現所需的功能。
    1. 裝置型別與行為
      裝置的基本行為:每個裝置透過實現computeOutput()方法來計算自己的輸出。裝置的輸出可能依賴於電路的特性(例如電壓、電流等),這些特性透過遞迴計算或傳遞不同的裝置資訊來處理。
      電路型別的區別:透過seriesMap和parallelMap來儲存串聯電路和並聯電路的配置資訊。串聯電路的電壓計算透過遞迴來實現,遞迴演算法基於電路的層次結構和裝置的輸出進行計算。
    1. 資料結構設計
      HashMap 與 List:
      HashMap被廣泛用於儲存裝置及電路資訊,方便透過ID快速查詢裝置。devices是一個HashMap,透過裝置的ID進行查詢,seriesMap和parallelMap儲存串聯電路和並聯電路。
      List(如commands、inputLines)用於動態儲存命令和輸入行,支援按需增加或移除元素。
      遞迴資料結構:
      電路的計算使用了遞迴方法computeVoltage(),這是基於電路中裝置間的依賴關係設計的。遞迴方式可以簡潔地處理層次結構,如串聯電路的電壓計算。
    1. 輸入與輸出處理
      使用者輸入:使用者透過命令列輸入裝置、命令及電路配置等資訊。輸入資訊透過Scanner進行讀取,並儲存在inputLines列表中。
      程式碼透過字串的split()和substring()方法解析輸入命令,從而提取裝置ID、電路型別等資訊。對格式錯誤的輸入會進行跳過(透過continue),確保程式的穩定性。
      輸出格式化:在輸出電壓和功率等數值時,使用String.format()方法來格式化輸出,確保數值保留特定的小數位數,增加了輸出的可讀性和一致性。
    1. 錯誤處理與容錯
      在輸入時,使用了try-catch來捕獲可能的轉換錯誤。例如,如果輸入的數字格式不正確(如嘗試將非數字的字串轉換為浮動數),程式會丟擲NumberFormatException並進行處理。
      對於無效的命令列輸入(如缺少必要的裝置或電路資訊),程式會使用continue跳過當前行,保證在不正確的輸入下程式不崩潰。
    1. 遞迴與計算
      遞迴計算電壓:在並聯電路的計算中,透過遞迴遍歷電路結構並計算每個裝置的電壓分配。遞迴呼叫有助於處理電路的巢狀結構(如裝置的串聯或並聯),實現了靈活且高效的計算。
  • 踩坑心得及改進建議

    1. 遞迴計算帶來的問題
      潛在問題:
      棧溢位:遞迴計算電壓和功率時,若電路結構較深(特別是在電路層級較高時),可能會導致棧溢位。遞迴深度過大時,程式將消耗過多的棧空間。
      效能問題:遞迴可能導致重複計算,特別是在並聯和串聯電路混合的情況下,電路層級較深時,效能可能會顯著下降。
      最佳化建議:
      改用迭代方式:將遞迴改為顯式的迭代處理,使用棧或者佇列來模擬遞迴呼叫,從而避免棧溢位問題,並提升效能。
      快取中間結果:可以透過快取(例如,使用HashMap儲存已計算的電壓和電流結果),避免重複計算相同的電路部分,提升效率。
    1. 命令列輸入的魯棒性
      潛在問題:
      輸入格式不規範:使用者輸入的命令可能存在格式不一致的問題(例如缺少必要的引數或順序錯誤),這些問題會導致程式執行失敗或輸出錯誤。
      錯誤處理不完善:儘管程式碼有try-catch處理輸入格式錯誤,但沒有進行詳細的錯誤資訊反饋,導致使用者無法知道錯誤原因。
      最佳化建議:
      更細緻的輸入驗證:在解析輸入之前,對每個命令進行格式校驗。例如,檢查每個裝置或電路配置是否完整,或者某些關鍵字是否存在,避免使用者輸入錯誤導致後續邏輯的混亂。
      提供更詳細的錯誤資訊:捕獲異常後,除了跳過不合法輸入外,最好給使用者提示輸入錯誤的具體位置和原因。可以透過日誌記錄來幫助開發者除錯。
    1. 程式碼的可讀性與維護性
      潛在問題:
      方法和類過於複雜:某些方法(如computeVoltage())可能涉及較為複雜的邏輯,且缺乏註釋,導致程式碼閱讀和理解困難。尤其是遞迴方法和計算邏輯較為密集的部分,難以快速定位問題。
      類和方法的職責不清晰:一些類和方法可能在一個層面上負責過多的功能(例如同時管理電路配置和計算),這可能導致類和方法職責不清晰,影響程式碼的可維護性。
      最佳化建議:
      分解複雜方法:將複雜的計算方法分解成多個較小的、職責單一的方法,減少每個方法的複雜度,並透過註釋來說明其功能。
      增強程式碼可讀性:新增更多的註釋和文件,尤其是在複雜的遞迴邏輯和電路計算部分,幫助其他開發者更容易理解程式碼。
      單一職責原則:每個類應儘量遵循單一職責原則,避免讓一個類同時負責過多的功能。比如,計算邏輯和輸入輸出處理可以分開到不同的類中,增強可擴充套件性和維護性。
    1. 資料結構的選擇和效能
      潛在問題:
      HashMap 過度使用:雖然HashMap對於查詢裝置非常方便,但在某些情況下,它的使用可能導致不必要的效能開銷(例如,頻繁建立和查詢HashMap)。
      記憶體佔用問題:在大規模電路和裝置配置時,大量的HashMap和List可能導致記憶體佔用過高,尤其是儲存裝置和電路配置資訊時。
      最佳化建議:
      最佳化資料結構:可以考慮根據實際的應用場景最佳化資料結構,例如如果裝置之間有很多相似特徵,可以考慮使用更輕量的結構(例如TreeMap或自定義的物件池)來減少記憶體佔用。
      懶載入機制:對於一些不常用的裝置或電路配置,可以採用懶載入的策略,只有在需要時才載入資料,從而減少記憶體佔用。
    1. 計算邏輯的擴充套件性
      潛在問題:
      不容易擴充套件新裝置型別:當前裝置型別較為固定,新增裝置時需要修改多個地方的程式碼。若裝置型別增多,程式碼會變得越來越難維護。
      硬編碼的裝置行為:裝置的計算邏輯目前可能是透過硬編碼的方式實現的,隨著裝置數量的增加,程式碼的維護成本會加大。
      最佳化建議:
      策略模式:使用策略模式來將不同裝置的計算邏輯封裝為獨立的策略類,從而讓裝置的行為更加靈活,易於擴充套件。每個裝置可以根據其型別選擇相應的計算策略,而無需修改原有的裝置類。
      工廠模式:使用工廠模式來動態建立裝置物件,根據不同的輸入配置選擇合適的裝置型別,減少手動修改程式碼的需求。
    1. 電路計算的最佳化
      潛在問題:
      串聯和並聯電路的計算複雜度:當前的計算方法可能沒有充分考慮電路配置的多樣性,且遞迴計算可能在電路複雜時帶來較高的計算複雜度。
      最佳化建議:
      分而治之:將串聯電路和並聯電路的計算拆開,分別使用不同的演算法進行處理。透過將這些計算分開處理,減少演算法的複雜度。
      批次計算與快取:對於多個電路同時計算的情況,可以採用批次計算的方式,並將已計算的中間結果進行快取,避免重複計算,提高計算效率。
    1. 併發與執行緒安全
      潛在問題:
      執行緒安全問題:如果此程式碼在多執行緒環境中執行(例如,多個使用者併發執行命令),可能會導致資料競爭問題。特別是在對共享資料結構(如HashMap、List)進行讀寫操作時,可能會出現併發問題。
      最佳化建議:
      執行緒安全機制:如果需要支援併發執行,可以考慮使用ConcurrentHashMap或CopyOnWriteArrayList等執行緒安全的資料結構來避免併發衝突。
      加鎖機制:在訪問共享資源時,使用適當的鎖機制來保證執行緒安全,尤其是在計算過程和裝置更新過程中。

總結

    1. 學到了什麼
      在本階段的學習中,我透過多次題目集的練習,涵蓋了物件導向程式設計、資料結構、遞迴演算法、命令列處理等多個核心內容,並逐步深入理解了Java的物件導向特性及相關技術點。
    • 物件導向程式設計:透過實際編寫裝置與電路模型,深入理解了類、繼承、多型和抽象等基本概念。透過繼承實現了不同裝置的擴充套件,利用多型實現了靈活的裝置互動,極大地增強了程式碼的可擴充套件性和可維護性。
      資料結構與集合:熟練掌握了HashMap、List等常用資料結構的使用,並理解了它們在實際問題中的應用。透過這些資料結構,能夠高效地儲存和管理裝置、命令、以及電路的配置。
      遞迴與演算法最佳化:在電路計算中,我學會了如何使用遞迴來處理複雜的電路層次結構,並嘗試最佳化了遞迴過程,減少冗餘計算,提升了程式效能。
    • 命令列處理與使用者輸入:透過解析命令列輸入,理解了如何處理使用者輸入、驗證輸入格式,以及如何進行錯誤處理和輸出格式化。這一部分讓我更加熟悉了Java中字串的操作和異常處理。
      這些知識點的掌握不僅增強了我的程式設計能力,也讓我對物件導向的思想和Java語言的應用有了更深入的瞭解。
    1. 需要進一步學習與研究的地方
    • 遞迴演算法的深入理解與最佳化:雖然我已經理解並使用了遞迴演算法,但對於更高效的遞迴最佳化,如尾遞迴和迭代的轉換、遞迴深度過大的最佳化,還需要進一步學習和實踐。
    • 設計模式的應用:在物件導向程式設計中,設計模式(如策略模式、工廠模式等)能夠有效地提升程式碼的靈活性和擴充套件性。對於如何靈活運用設計模式以解決實際問題,還需要進一步學習和應用。
    • Java併發程式設計:對於多執行緒和併發程式設計的理解仍然較為薄弱。對於高併發的環境下,如何保證執行緒安全、減少競爭,最佳化效能,需要深入學習並在實際程式設計中加以實踐。
      效能最佳化與記憶體管理:程式碼在處理大量資料時的效能最佳化、記憶體管理方面還存在改進空間,如何提高程式的執行效率和避免記憶體溢位等問題,是我接下來要重點研究的方向。
    1. 對教師、課程、作業、實驗、課上及課下組織方式的改進建議
    • 教師講解方式:
      案例驅動教學:教師可以透過具體的實際案例來引導學生思考如何應用程式設計概念,幫助學生在實際程式設計中理解理論知識。每個概念的講解後,最好能夠透過例題或專案展示其應用,使理論與實踐緊密結合。
      互動式教學:在講解複雜概念時,鼓勵學生提問和討論,增加課堂的互動性,幫助學生更好地消化理解每個知識點。
      課程內容設定:
    • 進階內容引入:可以將遞迴、設計模式和併發程式設計等進階內容提前引入課程,讓學生有機會提前接觸和了解更高階的程式設計技巧,為後續的學習打下基礎。
      專案驅動式學習:課程中可以設計更多的實際專案,讓學生透過專案的完成來加深對知識點的理解和掌握。透過專案實踐,學生可以更好地應用所學知識,解決實際問題。
      作業與實驗:
    • 分階段任務:作業可以設計為分階段的任務,逐步加深難度,既能幫助學生逐步掌握程式設計技能,又能避免一次性任務過於龐大導致學生失去動力。
      團隊協作任務:在實驗中引入小組協作任務,可以讓學生學習如何與他人協作、進行程式碼評審,提升團隊合作能力和溝通能力。
      課上與課下組織方式:
      課上討論與實踐結合:除了理論講解,教師可以留出更多時間進行課上討論,幫助學生解決在課下做作業時遇到的問題。透過討論,能夠促使學生獨立思考並加深對知識的理解。
      課下學習資源豐富化:課下可以提供更多的學習資源,如示例程式碼、解題思路、影片教程等,幫助學生更好地複習和鞏固所學內容。課程平臺上的討論區也可以作為一個良好的互動平臺,學生可以互相交流問題和學習經驗。