Skip to Content
Python✨ Clean Code & ArchitectureClean Code

Clean Code - Code Sạch

Clean Code là gì?

Clean Code là code được viết một cách rõ ràng, dễ đọc, dễ hiểu và dễ bảo trì. Đây không chỉ là về việc code chạy đúng, mà còn về việc code được viết tốt như thế nào.

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” - Martin Fowler

Đặt tên có nghĩa (Meaningful Names)

1. Tên biến rõ ràng

❌ Tên xấu:

# Không rõ nghĩa d = 86400 # Là gì? t = time.time() # Time gì? data = [...] # Data gì? # Quá ngắn, không có nghĩa a = 10 x = calculate(a)

✅ Tên tốt:

# Rõ nghĩa, dễ hiểu SECONDS_PER_DAY = 86400 current_timestamp = time.time() customer_orders = [...] # Mô tả rõ mục đích max_retry_attempts = 3 user_age = 25 is_authenticated = True

2. Tên hàm thể hiện hành động

❌ Tên xấu:

def data(user): # Làm gì? pass def process(item): # Process gì? pass def x(): # Không biết làm gì pass

✅ Tên tốt:

def get_user_profile(user_id: str) -> dict: """Lấy thông tin profile của user.""" pass def calculate_total_price(items: list) -> float: """Tính tổng giá của các items.""" pass def validate_email_format(email: str) -> bool: """Kiểm tra định dạng email có hợp lệ không.""" pass

3. Tên class là danh từ

❌ Tên xấu:

class ProcessData: # Động từ, không rõ pass class Manager: # Quá chung chung pass class DoSomething: # Không rõ làm gì pass

✅ Tên tốt:

class UserAccount: """Đại diện cho tài khoản người dùng.""" pass class OrderProcessor: """Xử lý các đơn hàng.""" pass class EmailValidator: """Validator cho email.""" pass

Hàm ngắn gọn và làm một việc

Nguyên tắc: Một hàm chỉ làm một việc

❌ Hàm làm nhiều việc:

def process_user_registration(user_data): # Validate if not user_data.get('email'): raise ValueError("Email is required") if len(user_data.get('password', '')) < 8: raise ValueError("Password too short") # Hash password import hashlib hashed = hashlib.sha256(user_data['password'].encode()).hexdigest() user_data['password'] = hashed # Save to database import sqlite3 conn = sqlite3.connect('users.db') cursor = conn.cursor() cursor.execute("INSERT INTO users VALUES (?, ?)", (user_data['email'], hashed)) conn.commit() # Send welcome email import smtplib server = smtplib.SMTP('smtp.gmail.com', 587) server.send_message(...) # Log print(f"User registered: {user_data['email']}") return True

✅ Tách thành nhiều hàm nhỏ:

from typing import Dict def validate_user_data(user_data: Dict) -> None: """Validate user registration data.""" if not user_data.get('email'): raise ValueError("Email is required") if len(user_data.get('password', '')) < 8: raise ValueError("Password must be at least 8 characters") def hash_password(password: str) -> str: """Hash password using SHA-256.""" import hashlib return hashlib.sha256(password.encode()).hexdigest() def save_user_to_database(email: str, hashed_password: str) -> None: """Save user to database.""" import sqlite3 with sqlite3.connect('users.db') as conn: cursor = conn.cursor() cursor.execute( "INSERT INTO users (email, password) VALUES (?, ?)", (email, hashed_password) ) conn.commit() def send_welcome_email(email: str) -> None: """Send welcome email to new user.""" # Email sending logic pass def log_user_registration(email: str) -> None: """Log user registration event.""" import logging logging.info(f"User registered: {email}") def register_user(user_data: Dict) -> bool: """ Register a new user. Args: user_data: Dictionary containing email and password Returns: True if registration successful """ validate_user_data(user_data) hashed_password = hash_password(user_data['password']) save_user_to_database(user_data['email'], hashed_password) send_welcome_email(user_data['email']) log_user_registration(user_data['email']) return True

Ưu điểm:

  • Mỗi hàm làm một việc rõ ràng
  • Dễ đọc, dễ hiểu
  • Dễ kiểm thử từng phần
  • Dễ tái sử dụng

Tránh Magic Numbers

❌ Magic Numbers:

def calculate_discount(price): if price > 1000000: return price * 0.1 elif price > 500000: return price * 0.05 return 0 # 1000000, 500000, 0.1, 0.05 là gì?

✅ Sử dụng constants:

# Constants với tên rõ nghĩa PREMIUM_THRESHOLD = 1_000_000 # VND STANDARD_THRESHOLD = 500_000 # VND PREMIUM_DISCOUNT_RATE = 0.10 # 10% STANDARD_DISCOUNT_RATE = 0.05 # 5% def calculate_discount(price: float) -> float: """ Calculate discount based on price. Args: price: Order price in VND Returns: Discount amount in VND """ if price > PREMIUM_THRESHOLD: return price * PREMIUM_DISCOUNT_RATE elif price > STANDARD_THRESHOLD: return price * STANDARD_DISCOUNT_RATE return 0

Comment đúng cách

Khi nào NÊN comment:

1. Giải thích “Tại sao” chứ không phải “Cái gì”

# ❌ Comment giải thích code rõ ràng (không cần) # Tăng i lên 1 i += 1 # ✅ Comment giải thích lý do # Thêm 1 vào index vì API trả về index bắt đầu từ 0 # nhưng database của chúng ta bắt đầu từ 1 database_index = api_index + 1

2. Warning về hậu quả

def delete_all_user_data(user_id: str) -> None: """ Delete all user data from system. WARNING: This operation is irreversible and will delete: - User profile - Order history - Payment information - All related data Use with extreme caution! """ pass

3. TODO và FIXME

def calculate_shipping_fee(distance: float) -> float: # TODO: Implement dynamic pricing based on fuel cost # FIXME: This doesn't handle international shipping base_rate = 20000 return base_rate + (distance * 5000)

Khi nào KHÔNG NÊN comment:

❌ Comment code rõ ràng:

# Lấy username username = user.get_username() # Kiểm tra nếu user active if user.is_active: # In ra welcome message print(f"Welcome {username}")

✅ Code tự giải thích:

username = user.get_username() if user.is_active: print(f"Welcome {username}")

Xử lý lỗi đúng cách

1. Sử dụng Exception thay vì return codes

❌ Return codes:

def divide(a, b): if b == 0: return None # Lỗi? return a / b result = divide(10, 0) if result is None: # Phải kiểm tra None print("Error")

✅ Exceptions:

def divide(a: float, b: float) -> float: """ Divide two numbers. Args: a: Numerator b: Denominator Returns: Result of division Raises: ValueError: If denominator is zero """ if b == 0: raise ValueError("Cannot divide by zero") return a / b try: result = divide(10, 0) except ValueError as e: print(f"Error: {e}")

2. Exception cụ thể

❌ Exception chung chung:

try: user_data = json.loads(data) user = create_user(user_data) save_user(user) except Exception as e: # Quá chung chung print("Something went wrong")

✅ Exception cụ thể:

import json from typing import Dict class UserCreationError(Exception): """Raised when user creation fails.""" pass class UserSaveError(Exception): """Raised when saving user to database fails.""" pass def process_user_data(data: str) -> None: try: user_data = json.loads(data) except json.JSONDecodeError as e: raise ValueError(f"Invalid JSON data: {e}") try: user = create_user(user_data) except Exception as e: raise UserCreationError(f"Failed to create user: {e}") try: save_user(user) except Exception as e: raise UserSaveError(f"Failed to save user: {e}")

Code Formatting

1. Consistent Indentation

# ✅ Consistent 4 spaces def calculate_total(items): total = 0 for item in items: if item.is_available: total += item.price return total

2. Line Length (< 80-100 characters)

# ❌ Quá dài def create_user_with_all_details(first_name, last_name, email, phone_number, address, city, country, postal_code): pass # ✅ Chia thành nhiều dòng def create_user_with_all_details( first_name: str, last_name: str, email: str, phone_number: str, address: str, city: str, country: str, postal_code: str ) -> User: pass

3. Blank Lines

# ✅ Sử dụng blank lines để phân tách logic def process_order(order_id: str) -> None: # Fetch order order = get_order(order_id) # Validate order if not order.is_valid(): raise ValueError("Invalid order") # Process payment payment = process_payment(order) # Update inventory update_inventory(order.items) # Send confirmation send_order_confirmation(order)

DRY: Don’t Repeat Yourself

❌ Lặp lại code:

def send_email_to_customers(customers): import smtplib server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() server.login('user@example.com', 'password') for customer in customers: server.send_message(...) server.quit() def send_email_to_admins(admins): import smtplib server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() server.login('user@example.com', 'password') for admin in admins: server.send_message(...) server.quit()

✅ Tái sử dụng code:

from typing import List import smtplib class EmailService: def __init__(self, smtp_host: str, smtp_port: int, username: str, password: str): self.smtp_host = smtp_host self.smtp_port = smtp_port self.username = username self.password = password def send_emails(self, recipients: List[str], message: str) -> None: """Send email to multiple recipients.""" with smtplib.SMTP(self.smtp_host, self.smtp_port) as server: server.starttls() server.login(self.username, self.password) for recipient in recipients: server.send_message(message) # Sử dụng email_service = EmailService('smtp.gmail.com', 587, 'user@example.com', 'password') email_service.send_emails(customers, customer_message) email_service.send_emails(admins, admin_message)

Type Hints

✅ Sử dụng type hints:

from typing import List, Dict, Optional def get_user_by_id(user_id: int) -> Optional[Dict]: """ Get user by ID. Args: user_id: The ID of the user Returns: User dictionary if found, None otherwise """ pass def calculate_average(numbers: List[float]) -> float: """Calculate average of numbers.""" if not numbers: return 0.0 return sum(numbers) / len(numbers)

Tổng kết

Checklist cho Clean Code:

  • Tên biến, hàm, class rõ nghĩa
  • Hàm ngắn gọn, làm một việc
  • Không có magic numbers
  • Comment giải thích “tại sao”, không phải “cái gì”
  • Xử lý exception cụ thể
  • Code formatting consistent
  • Không lặp lại code (DRY)
  • Sử dụng type hints
  • Có docstring cho hàm, class

Công cụ hữu ích:

  • pylint: Kiểm tra code quality
  • black: Auto-format code
  • mypy: Type checking
  • flake8: Style guide enforcement
# Cài đặt pip install pylint black mypy flake8 # Sử dụng pylint your_file.py black your_file.py mypy your_file.py flake8 your_file.py

Clean Code là một kỹ năng cần rèn luyện hàng ngày. Hãy bắt đầu áp dụng từng nguyên tắc một vào code của bạn!

Last updated on