1-2-4-代码是行为的集合而非逻辑的集合
1.2.4 代码是行为的集合而非逻辑的集合
引言
很多程序员写代码时关注的是"逻辑":
"如果条件A满足,执行操作1;否则执行操作2。"
但从系统的角度看,代码实际上定义的是"行为":
"当用户点击按钮时,系统应该展示什么?当数据无效时,系统应该如何响应?"
逻辑思维关注的是"如何实现",行为思维关注的是"系统对外界的响应"。
这个视角的转变会深刻改变你写代码的方式。
逻辑视角 vs 行为视角
逻辑视角:关注控制流
def process_order(order):
if order.status == 'pending':
if order.payment_verified:
if order.stock_available:
order.status = 'confirmed'
send_confirmation_email(order)
else:
order.status = 'out_of_stock'
else:
order.status = 'payment_failed'
elif order.status == 'confirmed':
if order.shipped:
order.status = 'delivered'
# ... 更多逻辑
这段代码关注的是:条件判断、状态转换、控制流。
问题:
- 很难一眼看出"系统会做什么"
- 嵌套层次深,难以理解
- 新增一个状态需要修改多处
行为视角:关注状态机
class Order:
def confirm_payment(self):
"""行为:确认支付"""
if self.status != 'pending':
raise InvalidStateTransition("只能确认待处理订单的支付")
if not self.stock_available:
raise OutOfStockError("商品库存不足")
self.status = 'payment_confirmed'
self._send_confirmation_email()
def ship(self):
"""行为:发货"""
if self.status != 'payment_confirmed':
raise InvalidStateTransition("只能发货已确认支付的订单")
self.status = 'shipped'
self._send_shipping_notification()
def deliver(self):
"""行为:送达"""
if self.status != 'shipped':
raise InvalidStateTransition("只能标记已发货订单为送达")
self.status = 'delivered'
self._send_delivery_confirmation()
这段代码关注的是:系统能做什么操作,每个操作的前置条件和后果。
优点:
- 每个方法对应一个"行为"
- 行为的前置条件和后果都很清晰
- 容易扩展新行为
- 容易测试(每个行为独立测试)
从逻辑到行为的思维转变
案例1:用户登录
逻辑思维:
def login(username, password):
user = db.get_user(username)
if user:
if user.is_active:
if check_password(password, user.password_hash):
session.set('user_id', user.id)
return True
else:
log_failed_attempt(username)
return False
else:
return False
else:
return False
关注:一系列 if-else 判断。
问题:
- 三层嵌套
- 多个返回点
- 不清楚"失败"的具体原因
- 没有区分"用户不存在"和"密码错误"
行为思维:
class LoginResult:
"""登录结果:封装登录行为的所有可能结果"""
def __init__(self, success, reason=None, user=None):
self.success = success
self.reason = reason
self.user = user
def login(username, password) -> LoginResult:
"""行为:用户登录"""
user = db.get_user(username)
if user is None:
log_failed_attempt(username, 'user_not_found')
return LoginResult(success=False, reason='用户不存在')
if not user.is_active:
log_failed_attempt(username, 'user_inactive')
return LoginResult(success=False, reason='账户已被禁用')
if not check_password(password, user.password_hash):
log_failed_attempt(username, 'wrong_password')
return LoginResult(success=False, reason='密码错误')
session.set('user_id', user.id)
log_successful_login(user)
return LoginResult(success=True, user=user)
关注:登录这个"行为"的所有可能结果。
优点:
- 每种失败都有明确的原因
- 扁平的结构,易于理解
- 容易添加新的失败情况(如"需要两步验证")
- 调用方可以根据不同原因展示不同提示
案例2:文件上传
逻辑思维:
def upload_file(file, user):
if file.size > MAX_SIZE:
return {'error': 'File too large'}
if not is_allowed_extension(file.name):
return {'error': 'Invalid file type'}
if not user.has_permission('upload'):
return {'error': 'Permission denied'}
file_path = save_to_disk(file)
db.create_file_record(user.id, file_path)
return {'success': True, 'file_id': record.id}
关注:一系列验证逻辑。
行为思维:
class FileUpload:
"""封装文件上传行为"""
def __init__(self, file, user):
self.file = file
self.user = user
def validate(self):
"""验证前置条件"""
errors = []
if self.file.size > MAX_SIZE:
errors.append(f'文件过大,最大允许 {MAX_SIZE/1024/1024}MB')
if not is_allowed_extension(self.file.name):
allowed = ', '.join(ALLOWED_EXTENSIONS)
errors.append(f'不支持的文件类型,仅支持: {allowed}')
if not self.user.has_permission('upload'):
errors.append('您没有上传权限')
return errors
def execute(self):
"""执行上传行为"""
errors = self.validate()
if errors:
raise ValidationError(errors)
file_path = self._save_to_disk()
record = self._create_database_record(file_path)
self._notify_user(record)
return record
def _save_to_disk(self):
"""私有方法:保存文件到磁盘"""
# ... 实现
def _create_database_record(self, file_path):
"""私有方法:创建数据库记录"""
# ... 实现
def _notify_user(self, record):
"""私有方法:通知用户"""
# ... 实现
关注:上传这个"行为"的完整流程。
优点:
- 验证逻辑和执行逻辑分离
- 每个步骤都有清晰的职责
- 容易测试每个步骤
- 容易扩展(如添加病毒扫描、生成缩略图等)
行为导向的设计原则
1. 用动词命名方法
| 逻辑导向 | 行为导向 |
|---|---|
process() |
approve_order() |
handle() |
send_notification() |
check() |
validate_email() |
do() |
cancel_subscription() |
行为导向的命名清楚表达了"系统会做什么"。
2. 每个方法对应一个用户操作或系统事件
# 不好:一个方法做多件事
def handle_user_action(user, action_type, data):
if action_type == 'login':
# 登录逻辑
elif action_type == 'logout':
# 登出逻辑
elif action_type == 'update_profile':
# 更新资料逻辑
# 好:每个行为一个方法
class UserService:
def login(self, username, password):
"""用户行为:登录"""
# ...
def logout(self, user):
"""用户行为:登出"""
# ...
def update_profile(self, user, data):
"""用户行为:更新资料"""
# ...
3. 明确前置条件和后置条件
def approve_order(order):
"""
行为:批准订单
前置条件:
- 订单状态必须是 'pending'
- 库存必须充足
- 用户必须有批准权限
后置条件:
- 订单状态变为 'approved'
- 发送确认邮件给客户
- 通知仓库准备发货
- 扣减库存
异常:
- InvalidStateError: 订单状态不是 pending
- OutOfStockError: 库存不足
- PermissionError: 用户无权限
"""
# 检查前置条件
if order.status != 'pending':
raise InvalidStateError("只能批准待处理订单")
if not has_sufficient_stock(order):
raise OutOfStockError("库存不足")
if not current_user.can_approve_orders():
raise PermissionError("无批准权限")
# 执行行为
order.status = 'approved'
send_confirmation_email(order.customer)
notify_warehouse(order)
deduct_stock(order.items)
# 后置条件已满足
4. 使用状态机思维
对于有明确状态的对象(订单、工作流、用户账户等),用状态机建模:
class Order:
# 允许的状态转换
TRANSITIONS = {
'pending': ['cancelled', 'payment_confirmed'],
'payment_confirmed': ['cancelled', 'shipped'],
'shipped': ['delivered', 'returned'],
'delivered': ['returned'],
'cancelled': [], # 终态
'returned': [], # 终态
}
def transition_to(self, new_status):
"""状态转换"""
allowed_transitions = self.TRANSITIONS.get(self.status, [])
if new_status not in allowed_transitions:
raise InvalidStateTransition(
f"不能从 {self.status} 转换到 {new_status}"
)
self.status = new_status
行为驱动开发(BDD)的启示
行为驱动开发(Behavior-Driven Development)强调用"行为"而非"逻辑"描述系统。
Given-When-Then 模式
# BDD 风格的测试
def test_user_can_login_with_valid_credentials():
# Given: 有一个激活的用户
user = create_user(username='alice', password='secret', is_active=True)
# When: 用户使用正确的凭证登录
result = login(username='alice', password='secret')
# Then: 登录成功
assert result.success is True
assert result.user == user
assert session.get('user_id') == user.id
def test_user_cannot_login_with_wrong_password():
# Given: 有一个激活的用户
user = create_user(username='alice', password='secret', is_active=True)
# When: 用户使用错误的密码登录
result = login(username='alice', password='wrong')
# Then: 登录失败,原因是密码错误
assert result.success is False
assert result.reason == '密码错误'
assert session.get('user_id') is None
这种测试风格关注的是"行为"而非实现细节。
对使用 AI 的程序员的建议
AI 倾向于生成"逻辑导向"的代码,因为训练数据中大量代码是这种风格。
如何让 AI 生成行为导向的代码
方法1:用行为描述需求
❌ 不好的提示:
"写一个函数检查用户是否可以访问资源"
✅ 好的提示:
"写一个方法 authorize_access(user, resource),实现以下行为:
- 如果用户是资源的所有者,允许访问
- 如果用户有管理员权限,允许访问
- 如果资源是公开的,允许访问
- 否则,抛出 PermissionError
每种情况都用清晰的 if 语句处理,不要嵌套"
方法2:要求状态机风格
"实现一个订单类,使用状态机模式:
- 状态包括:pending, confirmed, shipped, delivered, cancelled
- 实现这些行为方法:confirm(), ship(), deliver(), cancel()
- 每个方法检查当前状态是否允许转换
- 如果不允许,抛出 InvalidStateTransition 异常"
方法3:要求 BDD 风格的测试
"为这个登录功能写 BDD 风格的测试,使用 Given-When-Then 模式,覆盖以下场景:
1. 用户使用正确的凭证登录
2. 用户使用错误的密码登录
3. 用户账户被禁用
4. 用户不存在"
实践原则
1. 问自己:这段代码定义了什么行为?
写完一段代码后,问:
- 系统会对用户做什么?
- 系统会对外部事件如何响应?
- 这段代码的"行为"能用一句话描述吗?
如果不能,可能代码做了太多事情,需要拆分。
2. 用行为命名文件和模块
# 逻辑导向的组织
utils.py
helpers.py
common.py
# 行为导向的组织
user_authentication.py # 用户认证行为
order_processing.py # 订单处理行为
payment_handling.py # 支付处理行为
notification_sending.py # 通知发送行为
3. 在代码审查中关注行为
不好的 Code Review 评论:
"这个 if-else 太长了。"
好的 Code Review 评论:
"这个函数定义了3个不同的行为:验证用户、创建订单、发送通知。建议拆分成3个方法,每个方法对应一个清晰的行为,这样更容易理解和测试。"
真实案例:状态机的价值
GitHub Pull Request 的状态管理
GitHub 的 Pull Request 有清晰的状态和行为:
- 状态:open, merged, closed
- 行为:
open(): 创建 PRmerge(): 合并 PRclose(): 关闭 PRreopen(): 重新打开 PR
每个行为都有前置条件:
merge()只能在 PR 是open状态且通过所有检查时执行reopen()只能在 PR 是closed状态且未合并时执行
这种清晰的行为模型让系统易于理解和扩展。
检查清单
完成代码后,检查:
总结
代码是行为的集合:
- 逻辑关注"如何",行为关注"什么" —— 后者更容易理解
- 用行为命名方法 —— 动词短语,清楚表达系统会做什么
- 每个方法对应一个行为 —— 不要一个方法做多件事
- 明确前置条件和后置条件 —— 让行为的边界清晰
- 用状态机建模有状态的对象 —— 让状态转换显式化
- AI 需要指导才能生成行为导向的代码 —— 用行为描述需求
记住:
代码不是一系列if-else,而是系统对世界的响应方式。
写代码时,想的不应该是"怎么用逻辑实现",而应该是"系统应该表现出什么行为"。
行为导向的代码更接近人类思维,也更容易维护。

浙公网安备 33010602011771号