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(): 创建 PR
    • merge(): 合并 PR
    • close(): 关闭 PR
    • reopen(): 重新打开 PR

每个行为都有前置条件:

  • merge() 只能在 PR 是 open 状态且通过所有检查时执行
  • reopen() 只能在 PR 是 closed 状态且未合并时执行

这种清晰的行为模型让系统易于理解和扩展。

检查清单

完成代码后,检查:

总结

代码是行为的集合:

  1. 逻辑关注"如何",行为关注"什么" —— 后者更容易理解
  2. 用行为命名方法 —— 动词短语,清楚表达系统会做什么
  3. 每个方法对应一个行为 —— 不要一个方法做多件事
  4. 明确前置条件和后置条件 —— 让行为的边界清晰
  5. 用状态机建模有状态的对象 —— 让状态转换显式化
  6. AI 需要指导才能生成行为导向的代码 —— 用行为描述需求

记住:

代码不是一系列if-else,而是系统对世界的响应方式。

写代码时,想的不应该是"怎么用逻辑实现",而应该是"系统应该表现出什么行为"。

行为导向的代码更接近人类思维,也更容易维护。

posted @ 2025-11-29 21:54  Jack_Q  阅读(0)  评论(0)    收藏  举报