16 KiB
Clean Code Principles
整洁代码原则
Core Principles
核心原则
1. Meaningful Names
1. 有意义的命名
Variables: 变量:
# BAD
d = 10 # What is 'd'?
t = time.time()
# GOOD
elapsed_days = 10
current_timestamp = time.time()
Functions: 函数:
# BAD
def process(data):
pass
# GOOD
def calculate_user_average_score(user_scores):
pass
Classes: 类:
# BAD
class Data:
pass
# GOOD
class CustomerOrderProcessor:
pass
Boolean variables - use predicates: 布尔变量——使用谓词:
# BAD
flag = True
status = False
# GOOD
is_active = True
has_permission = False
can_edit = True
should_retry = False
2. Functions Should Do One Thing
2. 函数应该只做一件事
BAD - Multiple responsibilities: 反面示例——多重职责:
def process_user_data(user):
# Validate
if not user.email:
raise ValueError("Email required")
# Transform
user.name = user.name.upper()
# Save to database
db.save(user)
# Send email
email_service.send_welcome(user.email)
# Log
logger.info(f"User processed: {user.id}")
GOOD - Single responsibility: 正面示例——单一职责:
def validate_user(user):
if not user.email:
raise ValueError("Email required")
def normalize_user_data(user):
user.name = user.name.upper()
return user
def save_user(user):
db.save(user)
def send_welcome_email(email):
email_service.send_welcome(email)
def process_user_data(user):
validate_user(user)
user = normalize_user_data(user)
save_user(user)
send_welcome_email(user.email)
logger.info(f"User processed: {user.id}")
3. Keep Functions Small
3. 保持函数短小
Guideline: Aim for 10-20 lines per function. 指导原则:每个函数控制在 10–20 行。
BAD - 100+ line function: 反面示例——超过 100 行的函数:
def generate_report(users):
# 100 lines of mixed logic
# Filtering, sorting, formatting, calculations, file I/O
pass
GOOD - Extracted functions: 正面示例——提取后的函数:
def generate_report(users):
active_users = filter_active_users(users)
sorted_users = sort_by_activity(active_users)
report_data = calculate_statistics(sorted_users)
formatted_report = format_report(report_data)
save_report(formatted_report)
def filter_active_users(users):
return [u for u in users if u.is_active]
def sort_by_activity(users):
return sorted(users, key=lambda u: u.activity_score, reverse=True)
4. DRY (Don't Repeat Yourself)
4. DRY(不要重复自己)
BAD - Duplication: 反面示例——重复代码:
def calculate_student_grade(math_score, science_score):
if math_score >= 90:
math_grade = 'A'
elif math_score >= 80:
math_grade = 'B'
elif math_score >= 70:
math_grade = 'C'
else:
math_grade = 'F'
if science_score >= 90:
science_grade = 'A'
elif science_score >= 80:
science_grade = 'B'
elif science_score >= 70:
science_grade = 'C'
else:
science_grade = 'F'
return math_grade, science_grade
GOOD - Extract common logic: 正面示例——提取公共逻辑:
def score_to_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
return 'F'
def calculate_student_grade(math_score, science_score):
return score_to_grade(math_score), score_to_grade(science_score)
5. Avoid Magic Numbers
5. 避免魔数
BAD: 反面示例:
if age > 18:
can_vote = True
if len(password) < 8:
raise ValueError("Password too short")
GOOD: 正面示例:
VOTING_AGE = 18
MIN_PASSWORD_LENGTH = 8
if age > VOTING_AGE:
can_vote = True
if len(password) < MIN_PASSWORD_LENGTH:
raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")
6. Error Handling
6. 错误处理
BAD - Bare except, silent failures: 反面示例——裸 except、静默失败:
try:
result = risky_operation()
except:
pass # What went wrong?
GOOD - Specific exceptions, informative messages: 正面示例——具体异常、信息性消息:
try:
result = risky_operation()
except ValueError as e:
logger.error(f"Invalid value: {e}")
raise
except ConnectionError as e:
logger.error(f"Connection failed: {e}")
# Retry or fallback logic
7. Use Early Returns (Guard Clauses)
7. 使用提前返回(卫语句)
BAD - Nested conditions: 反面示例——嵌套条件:
def process_order(order):
if order is not None:
if order.is_valid():
if order.total > 0:
if order.customer.has_credit():
# Process order
return True
return False
GOOD - Early returns: 正面示例——提前返回:
def process_order(order):
if order is None:
return False
if not order.is_valid():
return False
if order.total <= 0:
return False
if not order.customer.has_credit():
return False
# Process order
return True
8. Comment Why, Not What
8. 注释说明「为什么」,而非「是什么」
BAD - Obvious comments: 反面示例——显而易见的注释:
# Increment i by 1
i += 1
# Loop through users
for user in users:
pass
GOOD - Explain non-obvious reasoning: 正面示例——解释非显而易见的理由:
# Use binary search because list is always sorted
# and can contain millions of items
index = binary_search(sorted_list, target)
# Cache for 5 minutes to reduce database load
# during peak hours (based on profiling data)
@cache(ttl=300)
def get_popular_products():
pass
9. Keep Indentation Shallow
9. 保持缩进深度较浅
BAD - Deep nesting: 反面示例——深层嵌套:
def process_data(items):
for item in items:
if item.is_valid():
if item.quantity > 0:
if item.price > 0:
if item.in_stock:
# Process
pass
GOOD - Use early returns, extraction: 正面示例——使用提前返回和提取:
def process_data(items):
for item in items:
if not should_process_item(item):
continue
process_item(item)
def should_process_item(item):
return (item.is_valid() and
item.quantity > 0 and
item.price > 0 and
item.in_stock)
10. Consistent Formatting
10. 一致的格式化
Use a formatter: Black (Python), Prettier (JavaScript), gofmt (Go) 使用格式化工具:Black (Python)、Prettier (JavaScript)、gofmt (Go)
Consistency matters: 一致性很重要:
# Pick one style and stick to it
# 选择一种风格并坚持使用
# Style 1
def foo(x, y, z):
return x + y + z
# Style 2
def foo(
x,
y,
z
):
return x + y + z
# Don't mix them randomly in the same file!
# 不要在同一个文件中随意混用!
SOLID Principles
SOLID 原则
S - Single Responsibility Principle
S——单一职责原则
A class should have one, and only one, reason to change. 一个类应该只有一个、且仅有一个变更理由。
BAD: 反面示例:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def save(self):
# Database logic
db.execute(f"INSERT INTO users...")
def send_email(self, message):
# Email logic
smtp.send(self.email, message)
GOOD: 正面示例:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
class UserRepository:
def save(self, user):
db.execute(f"INSERT INTO users...")
class EmailService:
def send_email(self, email, message):
smtp.send(email, message)
O - Open/Closed Principle
O——开闭原则
Open for extension, closed for modification. 对扩展开放,对修改关闭。
BAD: 反面示例:
class PaymentProcessor:
def process(self, payment_type, amount):
if payment_type == "credit_card":
# Credit card processing
pass
elif payment_type == "paypal":
# PayPal processing
pass
# Adding new type requires modifying this function!
GOOD: 正面示例:
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process(self, amount):
pass
class CreditCardPayment(PaymentMethod):
def process(self, amount):
# Credit card processing
pass
class PayPalPayment(PaymentMethod):
def process(self, amount):
# PayPal processing
pass
class PaymentProcessor:
def process(self, payment_method: PaymentMethod, amount):
payment_method.process(amount)
L - Liskov Substitution Principle
L——里氏替换原则
Subclasses should be substitutable for their base classes. 子类应该可以替换其基类。
BAD: 反面示例:
class Bird:
def fly(self):
print("Flying")
class Penguin(Bird):
def fly(self):
raise Exception("Penguins can't fly!")
GOOD: 正面示例:
class Bird:
def move(self):
pass
class FlyingBird(Bird):
def move(self):
self.fly()
def fly(self):
print("Flying")
class Penguin(Bird):
def move(self):
self.swim()
def swim(self):
print("Swimming")
I - Interface Segregation Principle
I——接口隔离原则
Clients should not depend on interfaces they don't use. 客户端不应该依赖它们不使用的方法。
BAD: 反面示例:
class Worker(ABC):
@abstractmethod
def work(self):
pass
@abstractmethod
def eat(self):
pass
class Robot(Worker):
def work(self):
print("Working")
def eat(self):
# Robots don't eat!
raise NotImplementedError
GOOD: 正面示例:
class Workable(ABC):
@abstractmethod
def work(self):
pass
class Eatable(ABC):
@abstractmethod
def eat(self):
pass
class Human(Workable, Eatable):
def work(self):
print("Working")
def eat(self):
print("Eating")
class Robot(Workable):
def work(self):
print("Working")
D - Dependency Inversion Principle
D——依赖倒置原则
Depend on abstractions, not concretions. 依赖抽象,而非具体实现。
BAD: 反面示例:
class MySQLDatabase:
def save(self, data):
pass
class UserService:
def __init__(self):
self.db = MySQLDatabase() # Tightly coupled
def save_user(self, user):
self.db.save(user)
GOOD: 正面示例:
class Database(ABC):
@abstractmethod
def save(self, data):
pass
class MySQLDatabase(Database):
def save(self, data):
pass
class PostgresDatabase(Database):
def save(self, data):
pass
class UserService:
def __init__(self, database: Database):
self.db = database # Depends on abstraction
def save_user(self, user):
self.db.save(user)
Code Smells to Avoid
需要避免的代码坏味
1. Long Parameter List
1. 过长的参数列表
# BAD
def create_user(name, email, phone, address, city, state, zip, country):
pass
# GOOD
class UserData:
def __init__(self, name, email, contact_info, address):
pass
def create_user(user_data: UserData):
pass
2. Primitive Obsession
2. 基本类型偏执
# BAD
def calculate_shipping(width, height, depth, weight):
pass
# GOOD
class Dimensions:
def __init__(self, width, height, depth):
self.width = width
self.height = height
self.depth = depth
class Package:
def __init__(self, dimensions, weight):
self.dimensions = dimensions
self.weight = weight
def calculate_shipping(package: Package):
pass
3. Feature Envy
3. 依恋情结
# BAD - Method in class A uses mostly data from class B
class Order:
def calculate_total(self, customer):
discount = customer.discount_rate
points = customer.loyalty_points
# Uses customer data extensively
pass
# GOOD - Move method to class B
class Customer:
def calculate_order_discount(self, order):
discount = self.discount_rate
points = self.loyalty_points
# Uses own data
pass
Testing Best Practices
测试最佳实践
1. AAA Pattern (Arrange-Act-Assert)
1. AAA 模式(Arrange-Act-Assert)
def test_user_creation():
# Arrange
name = "Alice"
email = "alice@example.com"
# Act
user = User(name, email)
# Assert
assert user.name == name
assert user.email == email
2. One Assertion Per Test (guideline)
2. 每个测试一个断言(指导原则)
# AVOID multiple unrelated assertions
def test_user():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
assert user.email == "alice@example.com"
assert user.is_valid()
assert user.created_at is not None
# PREFER focused tests
def test_user_name():
user = User("Alice", "alice@example.com")
assert user.name == "Alice"
def test_user_email():
user = User("Alice", "alice@example.com")
assert user.email == "alice@example.com"
3. Test Names Should Be Descriptive
3. 测试名称应具有描述性
# BAD
def test_user():
pass
# GOOD
def test_user_creation_with_valid_email_succeeds():
pass
def test_user_creation_with_invalid_email_raises_error():
pass
Refactoring Checklist
重构检查清单
When you see code that needs improvement: 当你看到需要改进的代码时:
-
Is it tested? If not, write tests first
-
One change at a time - Refactor incrementally
-
Run tests after each change - Ensure nothing breaks
-
Commit frequently - Small, focused commits
-
Don't change behavior - Refactoring should preserve functionality
-
有测试吗? 如果没有,先编写测试
-
一次只改一处——增量式重构
-
每次修改后运行测试——确保没有破坏任何功能
-
频繁提交——小而专注的提交
-
不改变行为——重构应保留原有功能
Key Takeaways
关键要点
-
Names matter - Spend time choosing good names
-
Functions should be small - Aim for 10-20 lines
-
One responsibility - Each function/class does one thing well
-
DRY - Don't repeat yourself
-
SOLID - Follow the five SOLID principles
-
Early returns - Reduce nesting with guard clauses
-
Comment why - Not what (code shows what)
-
Test - Write tests, refactor with confidence
-
命名很重要——花时间选择好的名称
-
函数应该短小——目标是 10–20 行
-
单一职责——每个函数/类做好一件事
-
DRY——不要重复自己
-
SOLID——遵循五大 SOLID 原则
-
提前返回——使用卫语句减少嵌套
-
注释说明「为什么」——而非「是什么」(代码本身就展示了是什么)
-
测试——编写测试,自信重构
Remember: Clean code is not about perfection—it's about making code easier to read, maintain, and extend! 记住:整洁代码不在于追求完美——而在于让代码更易读、更易维护、更易扩展!