From 274dfb62ea180f083290b59c6504f55f4085576a Mon Sep 17 00:00:00 2001 From: Awin Huang Date: Mon, 4 Aug 2025 17:52:30 +0800 Subject: [PATCH] vault backup: 2025-08-04 17:52:30 Affected files: 00.00 Inbox/clean code.md --- 00.00 Inbox/clean code.md | 440 +++++++++++++++++++++++++++++++++++++- 1 file changed, 436 insertions(+), 4 deletions(-) diff --git a/00.00 Inbox/clean code.md b/00.00 Inbox/clean code.md index 9d6c3ad..640f12f 100644 --- a/00.00 Inbox/clean code.md +++ b/00.00 Inbox/clean code.md @@ -7,7 +7,7 @@ description: --- # 核心設計原則 -本課程遵循3Rs架構: +遵循3Rs架構: 1. 可讀性(Readability):讓程式碼易於理解和維護 2. 可重用性(Reusability):減少重複程式碼,提高開發效率 3. 可重構性(Refactorability):支援模組化設計和持續改進 @@ -130,10 +130,149 @@ def process_user_data(user_data): # 記錄日誌 logger.info(f"User {user_data['name']} processed") ``` - + +## 避免副作用與Flag參數 +Good code: +```python +# 無副作用、單一職責的函式 +def transform_data(data): + """純函式:只負責資料轉換""" + return processed_data + +def save_processed_data(data): + """專門負責儲存資料""" + save_to_disk(data) + +def send_processing_notification(): + """專門負責發送通知""" + send_notification_email() + +def update_processing_count(): + """專門負責更新計數""" + global last_processed_count + last_processed_count += 1 +``` + +Bad code: +```python +# 帶有副作用和flag參數的糟糕函式 +def process_data(data, save_to_file=False, send_email=False): + # 處理資料 + processed_data = transform_data(data) + + # 副作用:修改全域變數 + global last_processed_count + last_processed_count += 1 + + # Flag參數導致函式做多件事 + if save_to_file: + save_to_disk(processed_data) + + if send_email: + send_notification_email() + + return processed_data +``` + - **程式碼組織結構** - 降層原則:由上到下的閱讀順序 - 錯誤處理:使用例外處理取代錯誤碼 + - 這點值得商榷,業界也爭論不休,不用硬性規定,應該依實際情況決定。 + +## 降層原則:由上到下的閱讀順序 +Good code: +```python +# 遵循降層原則的順序 +def main_process(): + """主要處理流程""" + setup_environment() + result = core_logic() + cleanup() + return result + +def setup_environment(): + """設定處理環境""" + # 設定環境 + pass + +def core_logic(): + """核心邏輯處理""" + return helper_function() + +def helper_function(): + """輔助函式""" + return "helper" + +def cleanup(): + """清理資源""" + # 清理 + pass +``` + +Bad code: +```python +# 混亂的函式順序 +def helper_function(): + return "helper" + +def main_process(): + setup_environment() + result = core_logic() + cleanup() + return result + +def setup_environment(): + # 設定環境 + pass + +def core_logic(): + return helper_function() + +def cleanup(): + # 清理 + pass +``` + +## 錯誤處理:使用例外處理取代錯誤碼 +這點值得商榷,業界也爭論不休,不用硬性規定,應該依實際情況決定。 +錯誤碼針對已知、能預測的錯誤,而例外則傾向於「不應該、不可能」的錯誤。 + +Good code: +```c +# 使用例外處理 +def process_file(filename): + """處理檔案,使用例外處理錯誤""" + try: + with open(filename, 'r') as file: + content = file.read() + return process_content(content) + except FileNotFoundError: + raise FileProcessingError(f"檔案 {filename} 不存在") + except PermissionError: + raise FileProcessingError(f"沒有權限讀取檔案 {filename}") + except Exception as e: + raise FileProcessingError(f"處理檔案時發生錯誤: {str(e)}") +``` + +Bad code: +```c +// 使用錯誤碼的方式 +int process_file(const char* filename) { + FILE* file = fopen(filename, "r"); + if (file == NULL) { + return -1; // 錯誤碼 + } + + char buffer[100]; + if (fread(buffer, 1, sizeof(buffer), file) == 0) { + fclose(file); + return -2; // 另一個錯誤碼 + } + + fclose(file); + return 0; // 成功 +} +``` - **註解最佳實踐** - 何時需要註解,何時不需要 @@ -152,6 +291,92 @@ def process_user_data(user_data): - **L**:里氏替換原則(LSP) - **I**:介面隔離原則(ISP) - **D**:依賴反轉原則(DIP) + +## 單一職責原則(SRP) +Good code: +```python +class User: + def __init__(self, username, email): + self.username = username + self.email = email + +class UserRepository: + def save(self, user): + database.save(user) + +class EmailService: + def send_email(self, email, message): + email_service.send(email, message) + +class UserReportGenerator: + def generate_report(self, user): + return f"User: {user.username}, Email: {user.email}" +``` + +Bad code: +```python +class User: + def __init__(self, username, email): + self.username = username + self.email = email + + def save_to_database(self): + # 儲存使用者到資料庫 + database.save(self) + + def send_email(self, message): + # 發送郵件 + email_service.send(self.email, message) + + def generate_report(self): + # 產生使用者報告 + return f"User: {self.username}, Email: {self.email}" +``` + +## 開放封閉原則(OCP) +Good code: +```python +from abc import ABC, abstractmethod + +class DiscountStrategy(ABC): + @abstractmethod + def calculate_discount(self, amount): + pass + +class RegularCustomerDiscount(DiscountStrategy): + def calculate_discount(self, amount): + return amount * 0.05 + +class PremiumCustomerDiscount(DiscountStrategy): + def calculate_discount(self, amount): + return amount * 0.10 + +class VIPCustomerDiscount(DiscountStrategy): + def calculate_discount(self, amount): + return amount * 0.15 + +class DiscountCalculator: + def __init__(self, strategy: DiscountStrategy): + self.strategy = strategy + + def calculate_discount(self, amount): + return self.strategy.calculate_discount(amount) +``` + +Bad code: +```python +class DiscountCalculator: + def calculate_discount(self, customer_type, amount): + if customer_type == "regular": + return amount * 0.05 + elif customer_type == "premium": + return amount * 0.10 + elif customer_type == "vip": + return amount * 0.15 + else: + return 0 +``` + - **實務應用案例** - 每個原則的程式碼範例 - 違反原則的常見問題 @@ -161,11 +386,143 @@ def process_user_data(user_data): - 策略模式:演算法替換 - 觀察者模式:事件處理 - 裝飾者模式:功能擴展 - + - 還有其他的設計模式,這裡不一一列出。設計模式可以給大家一個共同的語言,溝通可以更清楚、方便。 + +## 工廠模式:封裝物件創建 +Good code: +```python +from abc import ABC, abstractmethod + +class Transport(ABC): + @abstractmethod + def deliver(self): + pass + +class Car(Transport): + def deliver(self): + return "Delivering by car" + +class Bike(Transport): + def deliver(self): + return "Delivering by bike" + +class TransportFactory: + _transports = { + "car": Car, + "bike": Bike + } + + @classmethod + def create_transport(cls, transport_type): + transport_class = cls._transports.get(transport_type) + if not transport_class: + raise ValueError(f"Unknown transport type: {transport_type}") + return transport_class() +``` + +Bad code: +```python +# 直接創建物件,違反開放封閉原則 +def create_transport(transport_type): + if transport_type == "car": + return Car() + elif transport_type == "bike": + return Bike() + elif transport_type == "plane": + return Plane() + else: + raise ValueError("Unknown transport type") +``` + +## 策略模式:演算法替換 +Good code: +```python +class PaymentStrategy(ABC): + @abstractmethod + def process_payment(self, amount): + pass + +class CreditCardPayment(PaymentStrategy): + def process_payment(self, amount): + return f"Processing ${amount} via credit card" + +class PayPalPayment(PaymentStrategy): + def process_payment(self, amount): + return f"Processing ${amount} via PayPal" + +class PaymentProcessor: + def __init__(self, strategy: PaymentStrategy): + self.strategy = strategy + + def set_strategy(self, strategy: PaymentStrategy): + self.strategy = strategy + + def process_payment(self, amount): + return self.strategy.process_payment(amount) +``` + +Bad code: +```python +class PaymentProcessor: + def process_payment(self, amount, payment_type): + if payment_type == "credit_card": + # 信用卡處理邏輯 + return self._process_credit_card(amount) + elif payment_type == "paypal": + # PayPal處理邏輯 + return self._process_paypal(amount) + elif payment_type == "bank_transfer": + # 銀行轉帳處理邏輯 + return self._process_bank_transfer(amount) +``` + - **重構基本概念** - 重構定義:改善內部結構不影響外部行為 - 重構時機:三次法則與預備性重構 - 安全重構:依賴測試確保正確性 + +## 重構定義:改善內部結構不影響外部行為 +Good code: +```python +# 重構後:清晰且易於維護的程式碼 +def calculate_price(items): + return sum(calculate_item_price(item) for item in items) + +def calculate_item_price(item): + base_price = item['price'] * item['quantity'] + discount = get_discount_rate(item) + return base_price * (1 - discount) + +def get_discount_rate(item): + discount_rules = { + 'book': lambda qty: 0.1 if qty > 10 else 0, + 'electronics': lambda qty: 0.15 if qty > 5 else 0 + } + + rule = discount_rules.get(item['type'], lambda qty: 0) + return rule(item['quantity']) +``` + +Bad code: +```python +# 重構前:複雜且難以理解的程式碼 +def calculate_price(items): + total = 0 + for item in items: + if item['type'] == 'book': + if item['quantity'] > 10: + total += item['price'] * item['quantity'] * 0.9 + else: + total += item['price'] * item['quantity'] + elif item['type'] == 'electronics': + if item['quantity'] > 5: + total += item['price'] * item['quantity'] * 0.85 + else: + total += item['price'] * item['quantity'] + else: + total += item['price'] * item['quantity'] + return total +``` - **常用重構手法** - 提取方法(Extract Method) @@ -179,6 +536,66 @@ def process_user_data(user_data): - 單元測試的撰寫技巧 - 測試覆蓋率與品質指標 +## 紅燈-綠燈-重構循環 +Good code: +```python +# 正確的TDD方法:先寫測試 +import pytest + +# 1. 紅燈:先寫失敗的測試 +def test_factorial_of_zero(): + assert calculate_factorial(0) == 1 + +def test_factorial_of_positive_number(): + assert calculate_factorial(5) == 120 + +def test_factorial_of_negative_number(): + with pytest.raises(ValueError): + calculate_factorial(-1) + +# 2. 綠燈:寫最少的程式碼讓測試通過 +def calculate_factorial(n): + if n < 0: + raise ValueError("Factorial is not defined for negative numbers") + if n == 0: + return 1 + + result = 1 + for i in range(1, n + 1): + result *= i + return result + +# 3. 重構:改善程式碼品質 +def calculate_factorial(n): + """計算階乘值""" + if n < 0: + raise ValueError("Factorial is not defined for negative numbers") + + if n <= 1: + return 1 + + return n * calculate_factorial(n - 1) +``` + +Bad code: +```python +# 錯誤的TDD方法:先寫實作再寫測試 +def calculate_factorial(n): + if n < 0: + return None + if n == 0 or n == 1: + return 1 + result = 1 + for i in range(2, n + 1): + result *= i + return result + +# 之後才寫測試 +def test_factorial(): + assert calculate_factorial(5) == 120 + assert calculate_factorial(0) == 1 +``` + - **專案案例分析** - BMI計算機Clean Code版本實作 - 購物車系統的Clean Code改造 @@ -188,7 +605,22 @@ def process_user_data(user_data): - 審查清單建立 - 給予建設性回饋的技巧 - 處理團隊中程式碼品質分歧 - + +## 審查清單建立 +### Good +✅ 良好的程式碧審查評論: +- "這個函式做了太多事情,建議分解成較小的函式以提高可讀性" +- "考慮使用更描述性的變數名稱,例如將 'data' 改為 'user_profiles'" +- "這裡可能有空指標例外的風險,建議加上 null 檢查" +- "很好的重構!這樣的設計更容易測試和維護" +- "建議加上單元測試來覆蓋這個邊界情況" + +### Bad +❌ 糟糕的程式碼審查評論: +- "你的程式碼很爛" +- "為什麼要這樣寫?" +- "這個不對" +- "重寫整個函式" # 第四階段:進階實踐 - **效能最佳化考量**