# H01 季後賽 ## 本筆記網站 怕格式跑掉 https://hackmd.io/@ruserxd/BkLL8QB0R ## 本次目標 自行製作一個 mlb 季後賽表 首先,透過 json 檔案讀取例行賽的對戰紀錄表 -> 資料 接著,對資料進行處理, 有兩個聯盟 (American, National),每個聯盟有三個區域 (West, Central, East) 需要從每個聯盟挑出 2 個種子, 4 個外卡 每個區域 (Region) 都會有一個分區冠軍,從三個裡面選出兩個當作種子,剩下的一個為外卡 接著再從 3 個區域不是冠軍依照勝率 (高) 取出 3 對當做外卡 同分修改: 此次針對的數據為 2023 mlb 例行賽 因為有同勝場敗場的隊伍 我選擇修改數據 sry 原始兩隊數據為 wins:90, losses:72 ```json { "team": "Houston Astros", "wins": 91, "losses": 71 }, { "team": "Texas Rangers", "wins": 89, "losses": 73 } ``` ## Preventive Programming ### 1. SLF4J-Log4j 這部分算是個人習慣,平常使用 Spring Boot 框架,都會有 SLF4J 在裡頭 結果自己要導入的時候好麻煩,有些版本 1.多得,看文章說有出現安全性漏洞 不建議用,以下是後來找到的版本 ```xml <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.16</version> </dependency> ``` 還要設置對應的 log4j.properties 才能有希望呈現的時間顏色...(預設紅色) 這時才理解到 Spring Boot 的好 (當然還有其他的好) ```java // 獲得該區域的資料 private void getRegionsData(JSONObject jsonLeague, String region, HashMap<String, List<TeamData>> hashLeague) { ..省略 for (Object obj : teams) { ..大量程式碼省略 hashLeague.get(region).add(teamData); log.info("成功存入" + teamData); } } ``` Example: ![截圖 2024-09-29 晚上7.36.50](https://hackmd.io/_uploads/SyGl3hI00.png) ### 2. assert 為了避免出現資料的隊伍不足,可能今年一堆隊伍退出聯盟,因為內部沒錢 出現不符合一個聯盟 6 隊打季後賽的情況 設計了一段 assert 在 SortingRanking.java 上 `assert sortingLeague.size() == 6: "Need 6 teams for 本季季後賽,組別不夠打...";` `for (int i = 0; i < 3 && i < regionLoser.size(); i++)` **(此測試請使用src/main/resources/mlbTeamsJsonRecord/record_2023_forAssert.json)** ```java // 會先找每個區域的第一名出來, 把它放到對應的 Winner, 其餘放到 loser, 接著在按規則做排序 private ArrayList<TeamData> rankingLeague(HashMap<String, List<TeamData>> hashLeague) { ..大量程式碼省略 // 利用 winRate 進行排序 regionLoser.sort((a, b) -> Double.compare(b.winRate, a.winRate)); for (int i = 0; i < 3 && i < regionLoser.size(); i++) sortingLeague.add(regionLoser.get(i)); assert sortingLeague.size() == 6: "Need 6 teams for 本季季後賽,組別不夠打..."; return sortingLeague; } ``` Example: ![截圖 2024-09-29 晚上7.38.34](https://hackmd.io/_uploads/r1JUnnIRC.png) ### 3. try-cathch-finally 當我們在讀取 Json 檔案時,會希望 Json 是一個固定格式的檔案 當沒讀取到希望的聯盟名稱時會 throw 一段 error message 並結束時獲得一個訊息 **(此測試請使用src/main/resources/mlbTeamsJsonRecord/record_2023_forTryCatchFinally.json)** ```java // 聯盟 private static final String[] leagues = {"American League", "National League"}; // 獲取 Json 的資料 void getDataFromJsonFile(String filePath) { try { Object ob = new JSONParser().parse(new FileReader(filePath)); JSONObject js = (JSONObject) ob; for (String league : leagues) { // 這個聯盟的 Json 物件 JSONObject leagueJson = (JSONObject) js.get(league); // 如果沒找到對應的 league if (leagueJson == null) { throw new IllegalArgumentException("League not found in JSON: " + league); } ... 程式碼省略 } } catch (IOException | ParseException e) { log.error(e); } finally { log.info("抓取 Json 結束"); } } ``` Example: ![截圖 2024-09-29 晚上7.39.52](https://hackmd.io/_uploads/rkzs22IAA.png) ### 4. 三層式架構 我將整個架構分為三個步驟 (物件) 操作 - 步驟: 讀取 Json -> 對資料處理 -> 依照資料輸出結果 - 對應類別 Class: GetDataFromJson -> ShowMLBResult -> SortingRanking 這樣當哪個步驟出錯時,可以比較有效率的找出是哪裡的問題並修正,同時也較好閱讀程式碼 ## 其餘部分 ### model 對於 mlb 數據的設計 會有對應的隊伍名稱、勝場、敗場、勝率 運用一些 lombok 簡化我們需要寫的程式碼 ```java package org.example.model; import lombok.Builder; import lombok.Data; // lombok 簡化程式碼 @Builder @Data public class TeamData { public String teamName; public Long win; public Long lose; public Float winRate; } ``` ### 使用此專案 打開 Main 類別 -> run code 若想測試 Assert 請記得打上 -ea **又或是使用已設置的 MainRunner** 路徑可根據想測試的項目做使用 - 1 Assert (src/main/resources/mlbTeamsJsonRecord/record_2023_forAssert.json) - 2 try-catch-finally (src/main/resources/mlbTeamsJsonRecord/record_2023_forTryCatchFinally.json) - 3 Orignal version (src/main/resources/mlbTeamsJsonRecord/record_2023.json) 人性化設置 -> 透過輸入選擇即可 ```java public static void main(String[] args) { // 初始化可使用路徑 initial(); Scanner scanner = new Scanner(System.in); ShowMLBResult showMLBResult = new ShowMLBResult(); System.out.println("\n請問要執行哪個測試檔案"); System.out.println("1. Assert"); System.out.println("2. Try-Catch"); System.out.println("3. Original Version"); System.out.print("請輸入選項 (1-3): "); String input = scanner.nextLine(); switch (input) { case "1", "2", "3" -> { int index = Integer.parseInt(input) - 1; System.out.println("\n執行: " + paths.get(index)); showMLBResult.ShowMLBResult(paths.get(index)); } default -> System.out.println("無效的輸入"); } } ```