目錄
前言
設計與分析
PTA第四次作業
PTA第五次作業
PTA第六次作業
踩坑心得
總結
前言
這幾次的大作業總的來說都花了很多的時間,也有做不出來的,但是一直做總能學到一些什麼。每次我認為比較花時間的是怎麼去設計好類,第六次則要想清楚怎麼處理輸入的電器以及電器的電壓、電阻、甚至是電流,因為第六次和第五次不一樣,第五次是隻有串聯,而第六次則是串並混用。使用繼承還是用介面我認為都是可以做的,但有些情況是有更好的選擇,這幾次作業給我的感覺是,測試點要自己找,很多時候測試樣例過了,才有了一些分數。
設計分析
PTA第四次作業
第四次大作業比第三次大作業多了多選題的判題,我對多選題的設計我採用的是繼承Question類,定義了 Question、MultipleChoiceQuestion、FillInBlankQuestion、TestPaper、Student 、ExamSystem和 AnswerSheet 類來儲存題目、試卷、學生和答卷資訊。parseInput 方法解析輸入資訊並儲存到相應的資料結構中。checkTestPaperScores 方法檢查每張試卷的總分是否為100。gradeAnswers 方法對學生的答案進行評判,並生成輸出資訊。在 gradeAnswers 方法中生成答題資訊、判分資訊和提示資訊。
類圖:
順序圖:
設計思路
資料結構設計:
使用字典來儲存題目資訊、試卷資訊、學生資訊、答卷資訊和刪除題目資訊。
題目資訊字典:鍵為題目編號,值為題目物件(包含題目內容、標準答案、題目型別等)。
試卷資訊字典:鍵為試卷號,值為試卷物件(包含題目編號-分值對)。
學生資訊字典:鍵為學號,值為學生名字。
答卷資訊字典:鍵為試卷號-學號對,值為答案列表。
刪除題目集合:包含所有被刪除的題目編號。
功能模組劃分:
輸入解析模組:負責解析使用者輸入的各種資訊,將其轉換成系統內部的資料結構。
試卷檢查模組:檢查試卷總分是否為100分,如果不是則生成警示資訊。
答題判分模組:根據題目資訊和學生的答案計算分數,並生成詳細的答題資訊。
輸出模組:根據系統內部的資料結構生成符合要求的輸出格式。
異常處理:
對於輸入格式錯誤、題目引用錯誤、試卷號或學號引用錯誤等情況,需要設計相應的異常處理機制,確保系統的健壯性。
關鍵步驟
初始化資料結構:
建立題目資訊字典、試卷資訊字典、學生資訊字典、答卷資訊字典和刪除題目集合。
輸入處理:
迴圈讀取輸入直到遇到"end"。
根據輸入的第一字元(如#N、#T、#X、#S、#D)確定輸入型別,呼叫相應的方法處理輸入資訊。
對於題目資訊、試卷資訊、學生資訊、答卷資訊和刪除題目資訊分別進行解析並存入對應的資料結構。
試卷檢查:
遍歷試卷資訊字典,計算每張試卷的總分,如果不是100分則生成警示資訊。
答題判分:
對於每一份答卷,根據試卷資訊中的題目編號查詢題目資訊,對比學生的答案與標準答案,根據題目型別(選擇題、填空題等)的不同計算得分。
特別注意處理被刪除的題目、答案不存在、題目引用錯誤等情況。
生成輸出:
按照題目資訊、答題資訊、判分資訊、刪除題目提示資訊、題目引用錯誤提示資訊的順序生成輸出。
注意輸出順序優先順序為學號、試卷號,按從小到大的順序先按學號排序,再按試卷號。
異常處理:
在解析輸入資訊時檢查格式是否正確,對於格式錯誤的資訊生成錯誤提示。
在處理答題資訊時,檢查試卷號和學號是否存在,對於不存在的試卷號或學號生成相應的錯誤提示。
1.類設計:
Question: 基礎問題類,包含問題的ID、內容、答案和分數。
MultipleChoiceQuestion: 多選題類,繼承自Question,支援部分正確的情況。
FillInBlankQuestion: 填空題類,繼承自Question,支援大小寫不敏感的答案。
TestPaper: 試卷類,包含試卷ID和問題集合。
Student: 學生類,包含學生ID和姓名。
AnswerSheet: 答題卡類,包含試卷ID、學生ID和學生的答案集合。
2.系統設計
ExamSystem 是整個系統的主控類,負責解析輸入資料、管理題目、試卷、學生和答題卡,並進行評分。
3.輸入解析
ExamSystem 提供了一個 parseInput 方法來解析輸入資料。輸入資料包括題目、試卷、學生和答題卡的資訊,每種資訊都有特定的格式識別符號。
題目解析 (parseQuestion):
根據識別符號(#N:、#Z:、#K:)建立不同型別的問題物件。
儲存在 questions 集合中。
試卷解析 (parseTestPaper):
建立 TestPaper 物件,並將題目及其分數新增到試卷中。
儲存在 testPapers 集合中。
學生解析 (parseStudent):
建立 Student 物件,並儲存在 students 集合中。
答題卡解析 (parseAnswerSheet):
建立 AnswerSheet 物件,並將學生的答案新增到答題卡中。
儲存在 answerSheets 列表中。
刪除題目解析 (parseDeletedQuestion):
將已刪除題目的ID儲存在 deletedQuestions 集合中。
4.檢查試卷總分
checkTestPaperScores 方法遍歷所有試卷,檢查每個試卷的總分是否為100分,如果不是,則發出警告。
5.評分答案
gradeAnswers 方法遍歷所有答題卡,對每個答題卡進行評分:
獲取對應的試卷和學生資訊。
遍歷試卷中的每個題目,檢查學生的答案是否正確。
如果答案正確,累加題目分數。
如果是多選題或填空題且部分正確,累加部分分數。
輸出評分結果。
PTA第五次作業
類圖:
順序圖:
- 程式碼結構與邏輯
主要類和方法
Electric 類:
屬性:s(裝置型別識別符號)、id(裝置ID)、shuV(電壓)、ofopen(開關狀態)、speed(速度)、lin(連續值)。
方法:display()(顯示裝置狀態)、regulate(String vs)(調節裝置)、reshuV(double shuop)(設定電壓)、fanhui()(返回裝置型別的優先順序)。
子類:
Kaiguan(開關):
方法:display()、regulate(String vs)、reshuV(double shuop)。
Fendang(分檔調節器):
方法:display()、regulate(String vs)、reshuV(double shuop)。
Lianxu(連續調節器):
方法:display()、regulate(String vs)、reshuV(double shuop)。
Baichi(白熾燈):
方法:display()、reshuV(double shuop)。
Riguang(日光燈):
方法:display()、reshuV(double shuop)。
Diaoshan(吊扇):
方法:display()、reshuV(double shuop)。
主函式邏輯
輸入處理:
使用 Scanner 讀取標準輸入。
解析輸入資料,將連線資訊和控制命令分別儲存在 ArrayList 中。
裝置建立:
根據連線資訊建立相應的裝置物件,並儲存在 HashMap 中。
狀態更新:
根據控制命令更新裝置狀態。
處理開關的開閉狀態、分檔調節器的檔位變化、連續調節器的值設定等。
輸出結果:
將所有裝置按特定規則排序。
呼叫每個裝置的 display() 方法輸出最終狀態。 - 最佳化建議
異常處理
輸入驗證:確保輸入資料的格式正確,避免因輸入錯誤導致程式崩潰。
if (!s.startsWith("[") && !s.startsWith("#K") && !s.startsWith("#F") && !s.startsWith("#L")) {
System.err.println("Invalid input format: " + s);
continue;
}
程式碼最佳化
提取公共方法:減少重複程式碼,提高程式碼可維護性。
private static String extractDeviceId(String command) {
Pattern pattern = Pattern.compile("#(.*)");
Matcher matcher = pattern.matcher(command);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
效能最佳化
資料結構選擇:對於大規模資料,可以考慮使用更高效的資料結構,如 TreeMap 或 ConcurrentHashMap。
Map<String, Electric> map1 = new ConcurrentHashMap<>();
3. 擴充套件方向
圖形使用者介面
使用 Swing 或 JavaFX:提供圖形使用者介面,使使用者可以更直觀地操作和檢視電路狀態。
public class CircuitUI extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button("Run Simulation");
VBox vBox = new VBox(btn);
Scene scene = new Scene(vBox, 300, 250);
primaryStage.setTitle("Circuit Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
更多裝置型別
新增裝置:可以輕鬆新增更多型別的裝置,只需繼承 Electric 類並實現相應的方法。
class NewDevice extends Electric {
public NewDevice(String id) {
super("N", id);
}
@Override
public void display() {
System.out.printf("@N%s:%.2f\n", id, someProperty);
}
@Override
public void regulate(String vs) {
// 實現調節邏輯
}
@Override
public void reshuV(double shuop) {
// 實現電壓設定邏輯
}
@Override
public int fanhui() {
return 7; // 返回優先順序
}
}
多執行緒支援
非同步處理:對於複雜的電路模擬,可以考慮使用多執行緒來提高處理速度。
ExecutorService executor = Executors.newFixedThreadPool(4);
for (Electric device : arraylist6) {
executor.submit(() -> {
device.display();
});
}
executor.shutdown();
PTA第六次作業
順序圖
設計思路
類結構
介面和抽象類
Controllable 介面:定義了一個 control(String command) 方法,用於處理控制命令。
Device 抽象類:所有電器裝置的基類,定義了共有的屬性和方法,如裝置ID、輸入電壓、輸出電壓、電阻等。還定義了抽象方法 updateVoltage() 用於更新裝置的電壓狀態。
具體裝置類
Switch(開關):實現了 Controllable 介面,可以切換開閉狀態。
GearSpeedRegulator(分檔調速器):實現了 Controllable 介面,可以調節檔位。
ContinuousSpeedRegulator(連續調速器):實現了 Controllable 介面,可以調節連續值。
IncandescentLamp(白熾燈):根據電壓計算亮度。
FluorescentLight(日光燈):根據電壓確定亮度。
CeilingFan(吊扇):根據電壓計算轉速。
FloorFan(落地扇):根據電壓計算轉速。
電路類
SeriesCircuit(串聯電路):管理一組串聯的裝置,計算總電阻和電流。
ParallelCircuit(並聯電路):繼承自 Device 類,管理一組並聯的串聯電路,計算總電阻和電流。
輔助類
PinandDevice:負責處理輸入資料,建立裝置和電路物件,更新電路狀態,並列印裝置狀態。
inputHandle:負責讀取使用者輸入並呼叫 PinandDevice 的相關方法進行處理。
主要功能
輸入處理:
讀取使用者輸入,解析連線資訊和控制命令。
根據連線資訊建立裝置和電路物件。
根據控制命令更新裝置狀態。
電路連線:
串聯電路:管理一組串聯的裝置,計算總電阻和電流。
並聯電路:管理一組並聯的串聯電路,計算總電阻和電流。
狀態更新:
根據輸入電壓和電路連線關係,更新每個裝置的電壓和狀態。
處理控制命令,更新裝置的狀態。
輸出結果:
按照特定規則排序裝置,並輸出每個裝置的狀態。
詳細分析
- 類結構和繼承關係
Controllable 介面:定義了控制裝置的基本方法。
Device 抽象類:提供了裝置的基本屬性和方法,定義了抽象方法 updateVoltage()。
具體裝置類:每個裝置類繼承自 Device 類,實現了 updateVoltage() 方法,並根據需要實現了 control(String command) 方法。 - 輸入處理
PinandDevice 類:
processConnection_S:處理串聯電路的連線資訊,建立裝置和串聯電路物件。
processConnection_p:處理並聯電路的連線資訊,建立並聯電路物件。
createDevice:根據裝置ID建立具體的裝置物件。
processControlCommand:處理控制命令,更新裝置狀態。
Update_circuit_resistance:更新電路的總電阻和電流。
printDeviceStatus:按特定規則排序裝置,並輸出狀態。
inputHandle 類:
input:讀取使用者輸入,呼叫 PinandDevice 的方法處理連線資訊和控制命令。 - 電路連線和狀態更新
SeriesCircuit 類:
total_resistance:計算串聯電路的總電阻。
setCurrent:設定串聯電路的電流。
updateVoltage:更新串聯電路中每個裝置的電壓和狀態。
ParallelCircuit 類:
total_resistance:計算並聯電路的總電阻。
updateVoltage:更新並聯電路中每個串聯電路的電流和裝置的電壓和狀態。 - 輸出結果
PinandDevice 類:
printDeviceStatus:按特定規則排序裝置,並輸出狀態。
getTypeOrder:獲取裝置型別的優先順序,用於排序。
最佳化方案:
異常處理
輸入驗證:確保輸入資料的格式正確,避免因輸入錯誤導致程式崩潰。
if (!line.startsWith("[") && !line.startsWith("#T") && !line.startsWith("#M") && !line.startsWith("#")) {
System.err.println("Invalid input format: " + line);
continue;
}
程式碼最佳化
提取公共方法:減少重複程式碼,提高程式碼可維護性。
private static String extractDeviceId(String command) {
Pattern pattern = Pattern.compile("#(.*)");
Matcher matcher = pattern.matcher(command);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}
效能最佳化
資料結構選擇:對於大規模資料,可以考慮使用更高效的資料結構,如 TreeMap 或 ConcurrentHashMap。
Map<String, Device> devices = new ConcurrentHashMap<>();
擴充套件方向
圖形使用者介面
使用 Swing 或 JavaFX:提供圖形使用者介面,使使用者可以更直觀地操作和檢視電路狀態。
public class CircuitUI extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button("Run Simulation");
VBox vBox = new VBox(btn);
Scene scene = new Scene(vBox, 300, 250);
primaryStage.setTitle("Circuit Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
更多裝置型別
新增裝置:可以輕鬆新增更多型別的裝置,只需繼承 Device 類並實現相應的方法。
class NewDevice extends Device {
public NewDevice(String id) {
super(id, 0); // 初始電阻為0
}
@Override
public void updateVoltage() {
// 實現電壓更新邏輯
}
@Override
public void control(String command) {
// 實現控制邏輯
}
@Override
public String toString() {
return "@" + id + ":" + someProperty;
}
}
多執行緒支援
非同步處理:對於複雜的電路模擬,可以考慮使用多執行緒來提高處理速度。
ExecutorService executor = Executors.newFixedThreadPool(4);
for (Device device : sortedDevices) {
executor.submit(() -> {
System.out.println(device.toString());
});
}
executor.shutdown();
踩坑心得
1.一個類和方法只做一件事,做到低耦合,不能牽一髮而動全身
2.在方法中儘量少用選擇語句,如if-else,降低圈複雜度
3.在一些相似的類如果可以抽象出一些共同的屬性和方法,可以適當使用繼承和多型
4.要多去了解Java強大的類庫
5.需要加強自己對演算法的學習
6.使用繼承和多型可以大大降低程式碼複雜度,提高效率
總結
- 設計經驗
類設計與繼承
抽象類與介面:在第四次作業中,我們使用了抽象類Question和具體子類MultipleChoiceQuestion、FillInBlankQuestion,在第六次作業中使用了抽象類Device和介面Controllable。這些設計模式幫助我們更好地組織程式碼,提高複用性和擴充套件性。
單一職責原則:每個類和方法只做一件事,保持低耦合。例如,Question類只負責儲存和處理題目資訊,ExamSystem類負責整體流程控制。
資料結構設計
字典與集合:使用字典(如HashMap)儲存題目、試卷、學生和答題卡資訊,使用集合(如HashSet)儲存刪除的題目編號。這些資料結構使得資料查詢和管理更加高效。
多級巢狀結構:在第六次作業中,使用了多級巢狀的資料結構來管理串並聯電路,例如ParallelCircuit類中包含多個SeriesCircuit物件,這種設計能夠靈活地表示覆雜的電路結構。 - 實現經驗
輸入解析
解析策略:根據不同型別的輸入(如題目、試卷、學生、答題卡、控制命令等),使用不同的解析策略。透過字串匹配和正規表示式提取關鍵資訊。
異常處理:在解析過程中加入異常處理,確保輸入格式錯誤不會導致程式崩潰。例如,使用try-catch塊捕獲解析異常,並給出友好的錯誤提示。
功能模組劃分
模組化設計:將系統劃分為多個模組,每個模組負責一個特定的功能。例如,輸入解析模組、試卷檢查模組、答題判分模組和輸出模組。這種設計使得程式碼結構清晰,易於維護。
方法拆分:將長方法拆分成多個小方法,每個方法只完成一個具體任務。這樣可以降低方法的複雜度,提高可讀性和可測試性。 - 最佳化經驗
異常處理
輸入驗證:在解析輸入資料時,確保資料格式正確。例如,使用正規表示式驗證輸入字串的格式。
錯誤提示:對於格式錯誤或引用錯誤的資料,生成明確的錯誤提示資訊,幫助使用者快速定位問題。
程式碼最佳化
提取公共方法:減少重複程式碼,提高程式碼可維護性。例如,提取公共的字串解析方法extractDeviceId。
資料結構選擇:根據實際需求選擇合適的資料結構。例如,在處理大量資料時,使用ConcurrentHashMap代替普通的HashMap。
效能最佳化
多執行緒支援:對於複雜的電路模擬,可以使用多執行緒來提高處理速度。例如,使用ExecutorService非同步處理裝置狀態更新。
演算法最佳化:最佳化演算法,減少不必要的計算。例如,在計算電路總電阻和電流時,避免重複計算。 - 學習心得
物件導向程式設計
封裝:將資料和方法封裝在類中,提高程式碼的安全性和可維護性。
繼承和多型:透過繼承和多型,實現程式碼的複用和擴充套件。例如,Device類的子類可以重寫父類的方法,實現特定的裝置行為。
資料結構與演算法
常用資料結構:熟練掌握常用的Java資料結構,如HashMap、HashSet、ArrayList等。
演算法應用:學會在實際問題中應用合適的演算法,提高程式的效率。例如,使用排序演算法對裝置進行排序。
Java類庫
標準庫:熟悉Java標準庫中的常用類和方法,如Collections、Stream、Pattern、Matcher等。