Files
Obsidian-Main/00. Inbox/clean code.md
Awin Huang 0bb0434511 vault backup: 2025-10-07 15:16:30
Affected files:
00. Inbox/Study Toshiba TC956x/00. Inbox.md
00. Inbox/USB.canvas
00. Inbox/clean code.md
2025-10-07 15:16:30 +08:00

16 KiB
Raw Blame History

tags, aliases, date, time, description
tags aliases date time description
2025-08-04 16:49:36

核心設計原則

遵循3Rs架構

  1. 可讀性Readability讓程式碼易於理解和維護
  2. 可重用性Reusability減少重複程式碼提高開發效率
  3. 可重構性Refactorability支援模組化設計和持續改進

第一階段:基礎理論與核心原則

  • Clean Code定義與重要性
    • 什麼是Clean Code易讀、易懂、易維護的程式碼
    • 技術債務的概念與影響
    • 童子軍規則:讓程式碼比接手時更乾淨

Good code:

def calculate_doubled_value_plus_one(input_value):
    """計算輸入值的兩倍加一"""
    OFFSET = 1
    
    if input_value <= 0:
        raise ValueError("Input value must be positive")
    
    doubled_value = input_value * 2
    result = doubled_value + OFFSET
    return result

Bad code:

def f(x):
if x > 0:
    y = x * 2
    # TODO: fix this later
    z = y + 1  # magic number
    return z
else:
    return -1
  • 有意義的命名規範
    • 使用具描述性且明確的變數名稱
    • 函式命名:動詞片語表達動作意圖
    • 類別命名:名詞表達實體概念
    • 避免縮寫和神秘數字

Good code:

// 良好的命名
let elapsedTimeInDays;
let activeUsers = [];
let calculateTotalPrice = (price, quantity) => price * quantity;
def fetch_user_profile():
    return user_profile_data

def validate_user_credentials():
    # 驗證使用者憑證
    codes.....

Bad code:

// 糟糕的命名
let d;  // 經過的天數
let u = [];  // 使用者列表
let calc = (a, b) => a * b;
def data():
    return user_info

def process():
    # 處理某些東西
    pass
  • 函式設計原則
    • 單一職責原則:一個函式只做一件事
    • 函式長度控制目標在20行以下
    • 參數管理最多3個參數的建議
    • 避免副作用與Flag參數

一個函式只做一件事

Good code:

# 遵循單一職責的函式
def validate_user_data(user_data):
    """僅負責驗證使用者資料"""
    if not user_data.get('email'):
        raise ValueError("Email required")
    return True

def save_user_to_database(user_data):
    """僅負責儲存資料"""
    database.save(user_data)

def send_welcome_notification(email):
    """僅負責發送歡迎郵件"""
    email_service.send_welcome_email(email)

def log_user_registration(username):
    """僅負責記錄日誌"""
    logger.info(f"User {username} registered successfully")

Bad code:

# 違反單一職責的函式
def process_user_data(user_data):
    # 驗證資料
    if not user_data.get('email'):
        raise ValueError("Email required")
    
    # 儲存到資料庫
    database.save(user_data)
    
    # 發送通知郵件
    email_service.send_welcome_email(user_data['email'])
    
    # 記錄日誌
    logger.info(f"User {user_data['name']} processed")

避免副作用與Flag參數

Good code:

# 無副作用、單一職責的函式
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:

# 帶有副作用和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:

# 遵循降層原則的順序
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:

# 混亂的函式順序
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:

# 使用例外處理
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:

// 使用錯誤碼的方式
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;  // 成功
}
  • 註解最佳實踐

    • 何時需要註解,何時不需要
    • 讓程式碼自我說明
    • 有效註解的撰寫技巧
  • 程式碼格式化

    • 一致性的重要性
    • 縮排與空白字元的運用
    • 程式碼區塊的邏輯分組

第二階段:進階設計原則

  • 五大SOLID原則詳解
    • S單一職責原則SRP
    • O開放封閉原則OCP
    • L里氏替換原則LSP
    • I介面隔離原則ISP
    • D依賴反轉原則DIP

單一職責原則SRP

Good code:

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:

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:

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:

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
  • 實務應用案例

    • 每個原則的程式碼範例
    • 違反原則的常見問題
  • 常用設計模式

    • 工廠模式:封裝物件創建
    • 策略模式:演算法替換
    • 觀察者模式:事件處理
    • 裝飾者模式:功能擴展
    • 還有其他的設計模式,這裡不一一列出。設計模式可以給大家一個共同的語言,溝通可以更清楚、方便。

工廠模式:封裝物件創建

Good code:

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:

# 直接創建物件,違反開放封閉原則
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:

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:

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:

# 重構後:清晰且易於維護的程式碼
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:

# 重構前:複雜且難以理解的程式碼
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
    • 封裝成員變數Encapsulate Field
    • 方法更名Rename Method
    • 消除重複程式碼

第三階段:實戰應用

  • TDD基本概念與流程
    • 紅燈-綠燈-重構循環
    • 單元測試的撰寫技巧
    • 測試覆蓋率與品質指標

紅燈-綠燈-重構循環

Good code:

# 正確的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:

# 錯誤的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改造
    • 登錄註冊系統的重構實戰
  • 程式碼審查流程

    • 審查清單建立
    • 給予建設性回饋的技巧
    • 處理團隊中程式碼品質分歧

審查清單建立

Good

良好的程式碼審查評論:

  • "這個函式做了太多事情,建議分解成較小的函式以提高可讀性"
  • "考慮使用更描述性的變數名稱,例如將 'data' 改為 'user_profiles'"
  • "這裡可能有空指標例外的風險,建議加上 null 檢查"
  • "很好的重構!這樣的設計更容易測試和維護"
  • "建議加上單元測試來覆蓋這個邊界情況"

Bad

糟糕的程式碼審查評論:

  • "你的程式碼很爛"
  • "為什麼要這樣寫?"
  • "這個不對"
  • "重寫整個函式"

第四階段:進階實踐

  • 效能最佳化考量

    • 何時最佳化?
    • Clean Code與效能的權衡
    • 常見效能反模式避免
  • 建立個人程式碼品質標準

參考來源