--- tags: Note --- clean code 筆記 ==== ## 2.有意義的命名 ### 讓名稱代表意圖,使之名實相符 *如果一個名稱還需要註解的輔助,就不具備展現意圖的能力* ```python elapsed_time_in_days = None days_sinces_creation = None days_since_modification = None file_age_in_days = None ``` ```python def get_them(): list1 = [] for x in the_list: if x[0] == 4: list1.append(x) return list1 ``` 上述程式碼隱含著要求我們能夠回答下列問題: + the_list 存放著什麼型態 (type) 的東西? + the_list 裡 index 等於 0 的元素,代表的意義是什麼? + 數值 4 的意義是什麼? + 我們該如何使用回傳的 list1? 如果你發現很難以回答這些問題時,代表這段程式碼並沒有使用好的命名,它們應該要能夠展現這段程式碼想表達的意思。 實際上,現在正在開發的是一款踩地雷的遊戲,這個 get_them() 要做的事情是回傳已經被「插旗」的位置,所以我們可以這樣修改程式碼: ```python def get_flagged_cells(): flagged_cells = [] for cell in gameBoard: if cell.is_flagged(): flagged_cells.append(cell) return flagged_cells ``` ### 避免誤導 ``` string OwO, OiO, OuO int qwq, qnq, qpq float inl, in1, in7 ``` 避免這種無意義又容易混淆的命名 長命名比短命名好,常數做為命名,可以更簡單搜索到 但是如果可以用簡短的文字表達清楚,則短命名又比長命名好 ### 類別的命名 類別和物件應該使用 *名詞* 或 *名詞片語* 來命名,不應該是動詞 函式的命名以動詞為主 ## 3.函式 ### 簡短!!! 關於函式的首要準則,就是要簡短。 第二項準則,就是要比第一項的簡短函式還要更簡短。 ### 只做一件事情 一次只做一件事情 ex:洗澡需要洗頭洗身體洗腳洗臉,這都算是"洗澡"這一件事情,但是洗到一半去喝咖啡,這就不是同一件事情 switch 做超過一件事情,太冗長 switch可以埋在 "抽象工廠" 方法底下 ### 區塊和縮排 盡量不要有多層的巢狀結構 一個函式的if else 或 for 迴圈等等盡量不要超過兩層 ### 避免輸出型參數 ```python # Call: append_cell(game_baord) def append_cell(game_board): new_cell = Cell() game_board.append(new_cell) ``` ```python # Call: game_board.append_cell() def append_cell(self): new_cell = Cell() self.cells.append(new_cell) ``` 比較一下上面的寫法比較容易混淆,建議用下面的寫法 ## 4.註解 ### 註解無法彌補糟糕的程式碼 能夠直接在程式碼中表達意圖是最好的 ### 有益的註解 法律型註解,著作權聲明 TODO註解 解釋程式碼意圖 ## 5.編排 基本上照著PEP8就行了 ## 6.物件及資料結構 ### 資料結構與物件的差別 在這個章節中,作者解釋資料結構 (Data Structure) 與物件 (Object) 為何不同,而且它們是反對稱性的存在: + 資料結構:將資料直接暴露在外,可以直接對資料讀取與寫入。例如:list、dict、set 等。 + 物件:將資料隱藏起來,提供可以操作這些資料的函式在外面。例如: 🕸 Crawler、🕸 CrawlerRunner、 🕸 CrawlerProcess。 ### 德摩特爾法則 (The Law of Demeter) 德摩特爾法則又可以稱作「最少知識原則」 ``` 符合 LoD 的函式要求在物件 O 中的函式 m 只能調用以下幾種類型的函式: 1. O 本身 2. m 的參數 3. 在 m 中建立的物件 4. 宣告在 O 中的物件 5. 被 O 存取的全域變數,並且在 m 的 scope 中 ``` 範例 ```python e = E() class O: def __init__(self): self.a = A() def m(self, b): result = self.f() # O 本身的函式 b.execute() # m 的參數 d = D() # 在 m 中建立的物件 d.join() self.a.run() # 宣告在 O 中的物件 e.jump() # 被 O 存取的全域變數,並且在 m 的 scope 中 def f(self): return 1+1 view raw ``` ## 7.錯誤處理 ### 使用例外事件而非回傳錯誤碼 ```python class ExperimentController: ... def start_experiment(self): try: self._run_experiment() except ExperimentError as e: logging.error(e) def _run_experiment(self): self.experiment_worker.run() self.experiment_worker.close() class ExperimentWorker: ... def run(self): ... raise ExperimentError("Experiment didn't run.") ``` ### 包裝第三方函示庫可能拋出的例外事件 ```python URL = "..." try: page = requests.get(URL).text except HTTPError as e: report_error(e) logging.error(e) except ConnectionError as e: report_error(e) logging.error(e) except Timeout as e: report_error(e) logging.error(e) finally: ... ``` ### 不要回傳或傳遞null 傳遞或回傳 null 會讓程式變得很亂