vault backup: 2025-08-04 17:52:30
Affected files: 00.00 Inbox/clean code.md
This commit is contained in:
@@ -7,7 +7,7 @@ description:
|
|||||||
---
|
---
|
||||||
|
|
||||||
# 核心設計原則
|
# 核心設計原則
|
||||||
本課程遵循3Rs架構:
|
遵循3Rs架構:
|
||||||
1. 可讀性(Readability):讓程式碼易於理解和維護
|
1. 可讀性(Readability):讓程式碼易於理解和維護
|
||||||
2. 可重用性(Reusability):減少重複程式碼,提高開發效率
|
2. 可重用性(Reusability):減少重複程式碼,提高開發效率
|
||||||
3. 可重構性(Refactorability):支援模組化設計和持續改進
|
3. 可重構性(Refactorability):支援模組化設計和持續改進
|
||||||
@@ -131,9 +131,148 @@ def process_user_data(user_data):
|
|||||||
logger.info(f"User {user_data['name']} processed")
|
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)
|
- **L**:里氏替換原則(LSP)
|
||||||
- **I**:介面隔離原則(ISP)
|
- **I**:介面隔離原則(ISP)
|
||||||
- **D**:依賴反轉原則(DIP)
|
- **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,12 +386,144 @@ 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)
|
- 提取方法(Extract Method)
|
||||||
- 封裝成員變數(Encapsulate Field)
|
- 封裝成員變數(Encapsulate Field)
|
||||||
@@ -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版本實作
|
- BMI計算機Clean Code版本實作
|
||||||
- 購物車系統的Clean Code改造
|
- 購物車系統的Clean Code改造
|
||||||
@@ -189,6 +606,21 @@ def process_user_data(user_data):
|
|||||||
- 給予建設性回饋的技巧
|
- 給予建設性回饋的技巧
|
||||||
- 處理團隊中程式碼品質分歧
|
- 處理團隊中程式碼品質分歧
|
||||||
|
|
||||||
|
## 審查清單建立
|
||||||
|
### Good
|
||||||
|
✅ 良好的程式碧審查評論:
|
||||||
|
- "這個函式做了太多事情,建議分解成較小的函式以提高可讀性"
|
||||||
|
- "考慮使用更描述性的變數名稱,例如將 'data' 改為 'user_profiles'"
|
||||||
|
- "這裡可能有空指標例外的風險,建議加上 null 檢查"
|
||||||
|
- "很好的重構!這樣的設計更容易測試和維護"
|
||||||
|
- "建議加上單元測試來覆蓋這個邊界情況"
|
||||||
|
|
||||||
|
### Bad
|
||||||
|
❌ 糟糕的程式碼審查評論:
|
||||||
|
- "你的程式碼很爛"
|
||||||
|
- "為什麼要這樣寫?"
|
||||||
|
- "這個不對"
|
||||||
|
- "重寫整個函式"
|
||||||
|
|
||||||
# 第四階段:進階實踐
|
# 第四階段:進階實踐
|
||||||
- **效能最佳化考量**
|
- **效能最佳化考量**
|
||||||
|
|||||||
Reference in New Issue
Block a user