【设计模式】Python状态模式:从入门到实战

Python状态模式:告别订单状态的if-else地狱

前言

订单状态管理写过吗?待支付、已支付、已发货、已完成、已取消... 每个状态能做的操作不一样,状态之间的转换还有规则限制。

很多人的第一反应是 if-else

python 复制代码
if order.status == "pending":
    if action == "pay":
        order.status = "paid"
    elif action == "cancel":
        order.status = "cancelled"
elif order.status == "paid":
    if action == "ship":
        order.status = "shipped"
    # ...几十个分支

状态一多,这代码就没法看了。状态模式就是专门解决这个问题的------把每个状态的行为封装成独立的类,状态切换变得清晰可控

这篇文章从最简单的 Python 实现开始,然后用 transitions 库简化开发,最后手撸一个完整的 FastAPI 订单状态机。

🏠个人主页:山沐与山


文章目录


一、状态模式是什么

1.1 核心思想

状态模式让对象在内部状态改变时改变它的行为,看起来就像改变了它的类。

关键点:

  • 状态决定行为:不同状态下,同一个方法有不同的实现
  • 状态封装:每个状态是一个独立的类
  • 状态转换:由状态类自己控制能转换到哪些状态

1.2 订单状态流转图

复制代码
                    ┌──────────┐
         ┌─────────│  已取消   │
         │ cancel  └──────────┘
         │              ▲
         │              │ cancel (仅待支付可取消)
         │              │
    ┌────┴────┐    ┌────┴────┐    ┌─────────┐    ┌─────────┐
    │  待支付  │───▶│  已支付  │───▶│  已发货  │───▶│  已完成  │
    └─────────┘pay └─────────┘ship└─────────┘    └─────────┘
                                    confirm           ▲
                                       │              │
                                       └──────────────┘

你看,每个箭头都是一次状态转换,每次转换都有触发条件。

1.3 不用状态模式的噩梦

代码来自 bad_order.py

python 复制代码
class Order:
    def __init__(self):
        self.status = "pending"

    def pay(self):
        if self.status == "pending":
            self.status = "paid"
            print("[支付] 支付成功")
        elif self.status == "paid":
            raise Exception("订单已支付,请勿重复支付")
        elif self.status == "shipped":
            raise Exception("订单已发货,无法支付")
        elif self.status == "completed":
            raise Exception("订单已完成")
        elif self.status == "cancelled":
            raise Exception("订单已取消,无法支付")

    def ship(self):
        if self.status == "pending":
            raise Exception("订单未支付,无法发货")
        elif self.status == "paid":
            self.status = "shipped"
            print("[发货] 已发货")
        elif self.status == "shipped":
            raise Exception("订单已发货")
        elif self.status == "completed":
            raise Exception("订单已完成")
        elif self.status == "cancelled":
            raise Exception("订单已取消")

    def cancel(self):
        if self.status == "pending":
            self.status = "cancelled"
            print("[取消] 订单已取消")
        elif self.status == "paid":
            # 已支付取消要退款
            self.status = "cancelled"
            self._refund()
            print("[取消] 订单已取消,已退款")
        elif self.status == "shipped":
            raise Exception("订单已发货,无法取消")
        # ... 还有更多

    # 每加一个状态,所有方法都要改
    # 每加一个操作,要写一堆 if-else

问题在哪?

  • 代码膨胀:状态×操作 = 分支数量爆炸
  • 难以维护:改一个状态的逻辑要翻遍整个类
  • 容易出错 :漏掉某个分支就是 bug
  • 违反开闭原则:加新状态要改现有代码

二、Python基础实现

2.1 状态模式结构

先定义状态的抽象基类,代码来自 order_states.py

python 复制代码
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from datetime import datetime

if TYPE_CHECKING:
    from order import Order


class OrderState(ABC):
    """订单状态抽象基类"""

    name: str = "unknown"

    @abstractmethod
    def pay(self, order: "Order") -> None:
        pass

    @abstractmethod
    def ship(self, order: "Order") -> None:
        pass

    @abstractmethod
    def confirm(self, order: "Order") -> None:
        pass

    @abstractmethod
    def cancel(self, order: "Order") -> None:
        pass

    def _invalid_operation(self, operation: str):
        raise InvalidOperationError(
            f"状态 [{self.name}] 不支持操作 [{operation}]"
        )


class InvalidOperationError(Exception):
    """非法操作异常"""
    pass

看到没?每个状态都要实现 payshipconfirmcancel 这四个方法。

2.2 具体状态实现

2.2.1 待支付状态
python 复制代码
class PendingState(OrderState):
    """待支付状态"""

    name = "待支付"

    def pay(self, order: "Order") -> None:
        print("[支付] 处理支付...")
        # 调用支付接口
        order.paid_at = datetime.now()
        order.set_state(PaidState())
        print("[+] 支付成功")

    def ship(self, order: "Order") -> None:
        self._invalid_operation("发货")

    def confirm(self, order: "Order") -> None:
        self._invalid_operation("确认收货")

    def cancel(self, order: "Order") -> None:
        print("[取消] 取消订单...")
        order.cancelled_at = datetime.now()
        order.set_state(CancelledState())
        print("[+] 订单已取消")

这个状态下只能支付或取消,其他操作都会抛出异常。

2.2.2 已支付状态
python 复制代码
class PaidState(OrderState):
    """已支付状态"""

    name = "已支付"

    def pay(self, order: "Order") -> None:
        raise InvalidOperationError("订单已支付,请勿重复支付")

    def ship(self, order: "Order") -> None:
        print("[发货] 处理发货...")
        order.shipped_at = datetime.now()
        order.set_state(ShippedState())
        print("[+] 已发货")

    def confirm(self, order: "Order") -> None:
        self._invalid_operation("确认收货")

    def cancel(self, order: "Order") -> None:
        print("[取消] 取消订单,处理退款...")
        order._process_refund()
        order.cancelled_at = datetime.now()
        order.set_state(CancelledState())
        print("[+] 订单已取消,已退款")

已支付状态可以发货或取消,但取消时要退款。

2.2.3 已发货状态
python 复制代码
class ShippedState(OrderState):
    """已发货状态"""

    name = "已发货"

    def pay(self, order: "Order") -> None:
        self._invalid_operation("支付")

    def ship(self, order: "Order") -> None:
        raise InvalidOperationError("订单已发货")

    def confirm(self, order: "Order") -> None:
        print("[确认] 确认收货...")
        order.completed_at = datetime.now()
        order.set_state(CompletedState())
        print("[+] 订单完成")

    def cancel(self, order: "Order") -> None:
        raise InvalidOperationError("订单已发货,无法取消,请申请退货")
2.2.4 已完成和已取消状态
python 复制代码
class CompletedState(OrderState):
    """已完成状态"""

    name = "已完成"

    def pay(self, order: "Order") -> None:
        self._invalid_operation("支付")

    def ship(self, order: "Order") -> None:
        self._invalid_operation("发货")

    def confirm(self, order: "Order") -> None:
        raise InvalidOperationError("订单已完成")

    def cancel(self, order: "Order") -> None:
        raise InvalidOperationError("订单已完成,无法取消")


class CancelledState(OrderState):
    """已取消状态(终态)"""

    name = "已取消"

    def pay(self, order: "Order") -> None:
        raise InvalidOperationError("订单已取消")

    def ship(self, order: "Order") -> None:
        raise InvalidOperationError("订单已取消")

    def confirm(self, order: "Order") -> None:
        raise InvalidOperationError("订单已取消")

    def cancel(self, order: "Order") -> None:
        raise InvalidOperationError("订单已取消")

这两个是终态,所有操作都不允许。

2.3 订单上下文类

代码来自 order.py

python 复制代码
from datetime import datetime
from typing import Optional
from order_states import OrderState, PendingState


class Order:
    """订单(上下文)"""

    def __init__(self, order_id: str, amount: float):
        self.order_id = order_id
        self.amount = amount
        self.created_at = datetime.now()
        self.paid_at: Optional[datetime] = None
        self.shipped_at: Optional[datetime] = None
        self.completed_at: Optional[datetime] = None
        self.cancelled_at: Optional[datetime] = None

        # 初始状态
        self._state: OrderState = PendingState()

    @property
    def status(self) -> str:
        return self._state.name

    def set_state(self, state: OrderState) -> None:
        """切换状态"""
        old_state = self._state.name
        self._state = state
        print(f"   [状态转换] {old_state} → {state.name}")

    # 委托给状态对象处理
    def pay(self) -> None:
        self._state.pay(self)

    def ship(self) -> None:
        self._state.ship(self)

    def confirm(self) -> None:
        self._state.confirm(self)

    def cancel(self) -> None:
        self._state.cancel(self)

    def _process_refund(self) -> None:
        """处理退款"""
        print(f"   [退款] 退款 ¥{self.amount}")

    def __str__(self) -> str:
        return f"Order({self.order_id}, ¥{self.amount}, {self.status})"

关键点:Order 类把所有操作都委托给当前状态对象处理。

2.4 使用示例

代码来自 main.py

python 复制代码
from order import Order
from order_states import InvalidOperationError

# 创建订单
order = Order("ORD001", 299.00)
print(f"[创建] 创建订单: {order}\n")

# 支付
order.pay()
print(f"[当前] {order}\n")

# 发货
order.ship()
print(f"[当前] {order}\n")

# 确认收货
order.confirm()
print(f"[当前] {order}\n")

# 尝试非法操作
try:
    order.cancel()
except InvalidOperationError as e:
    print(f"[x] 操作失败: {e}")

输出:

复制代码
[创建] 创建订单: Order(ORD001, ¥299.0, 待支付)

[支付] 处理支付...
   [状态转换] 待支付 → 已支付
[+] 支付成功
[当前] Order(ORD001, ¥299.0, 已支付)

[发货] 处理发货...
   [状态转换] 已支付 → 已发货
[+] 已发货
[当前] Order(ORD001, ¥299.0, 已发货)

[确认] 确认收货...
   [状态转换] 已发货 → 已完成
[+] 订单完成
[当前] Order(ORD001, ¥299.0, 已完成)

[x] 操作失败: 订单已完成,无法取消

看到没?每个状态的行为都很清晰,状态转换也一目了然。


三、使用transitions库

手写状态类有点繁琐,Python 有个很好用的状态机库 transitions

3.1 安装和基本用法

bash 复制代码
pip install transitions

代码来自 order_with_transitions.py

python 复制代码
from transitions import Machine
from datetime import datetime


class Order:
    """订单状态机"""

    # 定义状态
    states = ['pending', 'paid', 'shipped', 'completed', 'cancelled']

    def __init__(self, order_id: str, amount: float):
        self.order_id = order_id
        self.amount = amount
        self.created_at = datetime.now()

        # 初始化状态机
        self.machine = Machine(
            model=self,
            states=Order.states,
            initial='pending',
            auto_transitions=False,  # 禁用自动转换
            send_event=True,  # 回调函数接收事件对象
        )

        # 定义转换
        self.machine.add_transition(
            trigger='pay',           # 触发方法名
            source='pending',        # 源状态
            dest='paid',             # 目标状态
            before='_before_pay',    # 转换前回调
            after='_after_pay'       # 转换后回调
        )

        self.machine.add_transition(
            trigger='ship',
            source='paid',
            dest='shipped',
            after='_after_ship'
        )

        self.machine.add_transition(
            trigger='confirm',
            source='shipped',
            dest='completed',
            after='_after_confirm'
        )

        self.machine.add_transition(
            trigger='cancel',
            source='pending',
            dest='cancelled',
            after='_after_cancel'
        )

        self.machine.add_transition(
            trigger='cancel',
            source='paid',
            dest='cancelled',
            before='_process_refund',
            after='_after_cancel'
        )

    # ========== 回调函数 ==========

    def _before_pay(self, event):
        print(f"[支付] 处理支付: 订单 {self.order_id}")

    def _after_pay(self, event):
        self.paid_at = datetime.now()
        print(f"[+] 支付成功")

    def _after_ship(self, event):
        self.shipped_at = datetime.now()
        print(f"[+] 已发货")

    def _after_confirm(self, event):
        self.completed_at = datetime.now()
        print(f"[+] 订单完成")

    def _after_cancel(self, event):
        self.cancelled_at = datetime.now()
        print(f"[+] 订单已取消")

    def _process_refund(self, event):
        print(f"[退款] 处理退款: ¥{self.amount}")

使用起来和之前一样:

python 复制代码
order = Order("ORD001", 299.00)
print(f"[初始] 初始状态: {order.state}")

order.pay()
print(f"[当前] 当前状态: {order.state}")

order.ship()
print(f"[当前] 当前状态: {order.state}")

# 检查是否可以执行某操作
print(f"\n[检查] 可以取消吗?{order.may_cancel()}")
print(f"[检查] 可以确认吗?{order.may_confirm()}")

order.confirm()
print(f"[当前] 当前状态: {order.state}")

transitions 库自动给我们生成了 may_xxx() 方法来检查操作是否允许。

3.2 带条件的转换

代码来自 order_with_conditions.py

python 复制代码
from transitions import Machine


class Order:
    states = ['pending', 'paid', 'shipped', 'completed', 'cancelled']

    def __init__(self, order_id: str, amount: float):
        self.order_id = order_id
        self.amount = amount
        self.is_vip = False
        self.stock_available = True

        self.machine = Machine(
            model=self,
            states=Order.states,
            initial='pending'
        )

        # 带条件的转换
        self.machine.add_transition(
            trigger='pay',
            source='pending',
            dest='paid',
            conditions=['_has_stock', '_payment_valid']  # 所有条件都满足才转换
        )

        # 根据条件转到不同状态
        self.machine.add_transition(
            trigger='ship',
            source='paid',
            dest='shipped',
            conditions='_can_ship'
        )

        # unless: 条件不满足时才转换
        self.machine.add_transition(
            trigger='cancel',
            source='pending',
            dest='cancelled',
            unless='_is_vip_order'  # 非VIP订单才能直接取消
        )

    # ========== 条件函数 ==========

    def _has_stock(self):
        """检查库存"""
        print("   [检查] 检查库存...")
        return self.stock_available

    def _payment_valid(self):
        """验证支付"""
        print("   [检查] 验证支付...")
        return True

    def _can_ship(self):
        """是否可发货"""
        return True

    def _is_vip_order(self):
        """是否VIP订单"""
        return self.is_vip

条件函数返回 True 才会触发转换,这样可以实现复杂的业务规则。

3.3 状态图可视化

transitions 还支持生成状态图,代码来自 visualize_states.py

python 复制代码
# 安装 graphviz: pip install graphviz
from transitions.extensions import GraphMachine


class Order:
    states = ['pending', 'paid', 'shipped', 'completed', 'cancelled']

    def __init__(self):
        # 使用 GraphMachine 代替 Machine
        self.machine = GraphMachine(
            model=self,
            states=Order.states,
            initial='pending',
            show_conditions=True,
            show_state_attributes=True
        )

        self.machine.add_transition('pay', 'pending', 'paid')
        self.machine.add_transition('ship', 'paid', 'shipped')
        self.machine.add_transition('confirm', 'shipped', 'completed')
        self.machine.add_transition('cancel', 'pending', 'cancelled')
        self.machine.add_transition('cancel', 'paid', 'cancelled')


order = Order()
# 生成状态图
order.get_graph().draw('order_state.png', prog='dot')
print("[+] 状态图已生成: order_state.png")

这会生成一个漂亮的状态转换图,方便理解和文档化。


四、FastAPI订单状态机实战

来构建一个生产级的订单状态机系统。

4.1 项目结构

复制代码
order_system/
├── main.py
├── models/
│   ├── __init__.py
│   └── order.py
├── schemas/
│   ├── __init__.py
│   └── order.py
├── state_machines/
│   ├── __init__.py
│   └── order_state_machine.py
├── services/
│   ├── __init__.py
│   └── order_service.py
└── repositories/
    ├── __init__.py
    └── order_repository.py

4.2 状态机定义

代码来自 state_machines/order_state_machine.py

python 复制代码
from transitions import Machine
from typing import Optional, Callable, List
from datetime import datetime
from enum import Enum


class OrderStatus(str, Enum):
    """订单状态枚举"""
    PENDING = "pending"
    PAID = "paid"
    SHIPPED = "shipped"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    REFUNDING = "refunding"
    REFUNDED = "refunded"


class OrderStateMachine:
    """订单状态机"""

    states = [s.value for s in OrderStatus]

    # 转换规则配置
    transitions = [
        # 支付
        {'trigger': 'pay', 'source': OrderStatus.PENDING.value,
         'dest': OrderStatus.PAID.value, 'conditions': '_can_pay'},

        # 发货
        {'trigger': 'ship', 'source': OrderStatus.PAID.value,
         'dest': OrderStatus.SHIPPED.value},

        # 确认收货
        {'trigger': 'confirm', 'source': OrderStatus.SHIPPED.value,
         'dest': OrderStatus.COMPLETED.value},

        # 取消(待支付)
        {'trigger': 'cancel', 'source': OrderStatus.PENDING.value,
         'dest': OrderStatus.CANCELLED.value},

        # 取消(已支付)-> 走退款流程
        {'trigger': 'cancel', 'source': OrderStatus.PAID.value,
         'dest': OrderStatus.REFUNDING.value},

        # 退款完成
        {'trigger': 'refund_complete', 'source': OrderStatus.REFUNDING.value,
         'dest': OrderStatus.REFUNDED.value},

        # 申请退货退款(已发货)
        {'trigger': 'request_refund', 'source': OrderStatus.SHIPPED.value,
         'dest': OrderStatus.REFUNDING.value},

        # 退款完成后标记取消
        {'trigger': 'finalize_cancel', 'source': OrderStatus.REFUNDED.value,
         'dest': OrderStatus.CANCELLED.value},
    ]

    def __init__(
        self,
        order_id: str,
        initial_state: str = OrderStatus.PENDING.value,
        on_state_change: Optional[Callable] = None
    ):
        self.order_id = order_id
        self.on_state_change = on_state_change
        self.transition_history: List[dict] = []

        self.machine = Machine(
            model=self,
            states=self.states,
            transitions=self.transitions,
            initial=initial_state,
            auto_transitions=False,
            after_state_change='_on_state_changed'
        )

    def _can_pay(self) -> bool:
        """支付条件检查"""
        # 实际项目中可能检查:库存、价格变化等
        return True

    def _on_state_changed(self):
        """状态变更回调"""
        record = {
            'order_id': self.order_id,
            'to_state': self.state,
            'timestamp': datetime.now().isoformat()
        }
        self.transition_history.append(record)

        if self.on_state_change:
            self.on_state_change(self.order_id, self.state)

    def get_available_actions(self) -> List[str]:
        """获取当前状态可用的操作"""
        return [t.name for t in self.machine.get_triggers(self.state)]

    def can_transition_to(self, target_state: str) -> bool:
        """检查是否可以转换到目标状态"""
        for trigger in self.machine.get_triggers(self.state):
            transitions = self.machine.get_transitions(trigger, self.state)
            for t in transitions:
                if t.dest == target_state:
                    return True
        return False

4.3 数据模型

代码来自 models/order.py

python 复制代码
from datetime import datetime
from typing import Optional
from pydantic import BaseModel


class Order(BaseModel):
    """订单模型"""
    id: str
    user_id: int
    items: list
    amount: float
    status: str
    created_at: datetime
    updated_at: Optional[datetime] = None
    paid_at: Optional[datetime] = None
    shipped_at: Optional[datetime] = None
    completed_at: Optional[datetime] = None
    cancelled_at: Optional[datetime] = None

    class Config:
        from_attributes = True

代码来自 schemas/order.py

python 复制代码
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime


class OrderCreate(BaseModel):
    """创建订单请求"""
    user_id: int
    items: list
    amount: float


class OrderResponse(BaseModel):
    """订单响应"""
    id: str
    user_id: int
    amount: float
    status: str
    available_actions: List[str]
    created_at: datetime


class OrderActionResponse(BaseModel):
    """订单操作响应"""
    order_id: str
    action: str
    old_status: str
    new_status: str
    message: str
    available_actions: List[str]

4.4 订单服务

代码来自 services/order_service.py

python 复制代码
from typing import Optional, List
from fastapi import HTTPException, status
from datetime import datetime

from models.order import Order
from schemas.order import OrderCreate, OrderResponse, OrderActionResponse
from state_machines.order_state_machine import OrderStateMachine, OrderStatus
from repositories.order_repository import OrderRepository


class OrderService:
    """订单服务"""

    def __init__(self, order_repo: OrderRepository):
        self.order_repo = order_repo

    def create_order(self, data: OrderCreate) -> OrderResponse:
        """创建订单"""
        order = Order(
            id=self._generate_order_id(),
            user_id=data.user_id,
            items=data.items,
            amount=data.amount,
            status=OrderStatus.PENDING.value,
            created_at=datetime.now()
        )
        order = self.order_repo.add(order)
        return self._to_response(order)

    def get_order(self, order_id: str) -> OrderResponse:
        """获取订单"""
        order = self._get_order_or_404(order_id)
        return self._to_response(order)

    def pay_order(self, order_id: str) -> OrderActionResponse:
        """支付订单"""
        return self._execute_action(order_id, 'pay', "支付成功")

    def ship_order(self, order_id: str) -> OrderActionResponse:
        """发货"""
        return self._execute_action(order_id, 'ship', "发货成功")

    def confirm_order(self, order_id: str) -> OrderActionResponse:
        """确认收货"""
        return self._execute_action(order_id, 'confirm', "确认收货成功")

    def cancel_order(self, order_id: str) -> OrderActionResponse:
        """取消订单"""
        return self._execute_action(order_id, 'cancel', "订单已取消")

    def get_available_actions(self, order_id: str) -> List[str]:
        """获取可用操作"""
        order = self._get_order_or_404(order_id)
        sm = self._get_state_machine(order)
        return sm.get_available_actions()

    # ========== 私有方法 ==========

    def _execute_action(
        self,
        order_id: str,
        action: str,
        success_message: str
    ) -> OrderActionResponse:
        """执行状态转换"""
        order = self._get_order_or_404(order_id)
        sm = self._get_state_machine(order)

        # 检查是否可以执行操作
        trigger_method = getattr(sm, f'may_{action}', None)
        if trigger_method and not trigger_method():
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"当前状态 [{order.status}] 不支持操作 [{action}]"
            )

        old_status = order.status

        # 执行状态转换
        try:
            getattr(sm, action)()
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=str(e)
            )

        # 更新数据库
        order.status = sm.state
        order.updated_at = datetime.now()
        self.order_repo.update(order)

        return OrderActionResponse(
            order_id=order_id,
            action=action,
            old_status=old_status,
            new_status=sm.state,
            message=success_message,
            available_actions=sm.get_available_actions()
        )

    def _get_order_or_404(self, order_id: str) -> Order:
        order = self.order_repo.get_by_id(order_id)
        if not order:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="订单不存在"
            )
        return order

    def _get_state_machine(self, order: Order) -> OrderStateMachine:
        return OrderStateMachine(
            order_id=order.id,
            initial_state=order.status
        )

    def _to_response(self, order: Order) -> OrderResponse:
        sm = self._get_state_machine(order)
        return OrderResponse(
            id=order.id,
            user_id=order.user_id,
            amount=order.amount,
            status=order.status,
            available_actions=sm.get_available_actions(),
            created_at=order.created_at
        )

    def _generate_order_id(self) -> str:
        """生成订单ID"""
        import uuid
        return f"ORD{uuid.uuid4().hex[:10].upper()}"

4.5 API 路由

代码来自 main.py

python 复制代码
from fastapi import FastAPI, Depends
from typing import List

from services.order_service import OrderService
from schemas.order import OrderCreate, OrderResponse, OrderActionResponse
from repositories.order_repository import OrderRepository


# 依赖注入
def get_order_service():
    repo = OrderRepository()
    return OrderService(repo)


app = FastAPI(title="订单状态机示例")


@app.post("/orders", response_model=OrderResponse)
def create_order(
    data: OrderCreate,
    service: OrderService = Depends(get_order_service)
):
    """创建订单"""
    return service.create_order(data)


@app.get("/orders/{order_id}", response_model=OrderResponse)
def get_order(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """获取订单详情"""
    return service.get_order(order_id)


@app.get("/orders/{order_id}/actions", response_model=List[str])
def get_available_actions(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """获取订单可用操作"""
    return service.get_available_actions(order_id)


@app.post("/orders/{order_id}/pay", response_model=OrderActionResponse)
def pay_order(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """支付订单"""
    return service.pay_order(order_id)


@app.post("/orders/{order_id}/ship", response_model=OrderActionResponse)
def ship_order(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """发货"""
    return service.ship_order(order_id)


@app.post("/orders/{order_id}/confirm", response_model=OrderActionResponse)
def confirm_order(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """确认收货"""
    return service.confirm_order(order_id)


@app.post("/orders/{order_id}/cancel", response_model=OrderActionResponse)
def cancel_order(
    order_id: str,
    service: OrderService = Depends(get_order_service)
):
    """取消订单"""
    return service.cancel_order(order_id)

4.6 响应示例

json 复制代码
// GET /orders/ORD001
{
    "id": "ORD001",
    "user_id": 1,
    "amount": 299.00,
    "status": "pending",
    "available_actions": ["pay", "cancel"],
    "created_at": "2024-01-15T10:30:00"
}

// POST /orders/ORD001/pay
{
    "order_id": "ORD001",
    "action": "pay",
    "old_status": "pending",
    "new_status": "paid",
    "message": "支付成功",
    "available_actions": ["ship", "cancel"]
}

看到没?通过状态机,我们可以动态地告诉前端当前订单能做哪些操作。


五、状态模式vs策略模式

这两个模式经常被混淆,因为结构很像。

方面 状态模式 策略模式
目的 管理对象的状态转换 封装可互换的算法
状态/策略数量 通常固定,和业务状态对应 可能很多,按需添加
切换时机 运行时自动切换(状态转换) 初始化时或运行时手动切换
切换者 状态对象自己决定下一个状态 客户端或上下文决定用哪个策略
典型场景 订单状态、工作流、TCP连接 支付方式、排序算法、压缩算法

代码对比:

python 复制代码
# 状态模式:状态决定下一个状态
class PaidState:
    def ship(self, order):
        order.set_state(ShippedState())  # 状态自己决定转换

# 策略模式:外部决定使用哪个策略
class PaymentService:
    def pay(self, strategy: PaymentStrategy):
        strategy.pay()  # 调用方决定用什么策略

简单说:状态模式关注状态转换 ,策略模式关注算法替换


六、总结

状态模式的核心是把状态相关的行为封装到独立的状态类中,让状态转换变得清晰可控。

什么时候用:

  • 对象行为取决于状态,且状态会在运行时改变
  • 代码中有大量与状态相关的条件判断
  • 状态转换规则复杂,需要清晰管理

实现方式对比:

方式 优点 缺点 适用场景
手写状态类 完全控制,逻辑清晰 代码量大 状态少,逻辑复杂
transitions 配置式,支持可视化 需要学习库 状态多,规则复杂
状态机框架 功能强大,持久化 过于重量级 企业级应用

关键要点总结:

概念 说明 适用场景 注意事项
状态封装 每个状态独立的类 状态行为差异大 避免状态类过多
状态转换 由状态对象控制 转换规则复杂 防止循环依赖
上下文对象 委托给状态处理 所有模式通用 保持接口一致
条件转换 带条件的状态切换 业务规则复杂 条件函数要简单

和其他模式配合:

  • 状态可以用单例(无状态的状态对象)
  • 状态创建可以用工厂模式
  • 状态转换可以触发观察者通知

下一步 :下一篇可以聊聊适配器模式 ,在对接第三方 API、兼容旧系统时特别有用。


热门专栏推荐

等等等还有许多优秀的合集在主页等着大家的光顾,感谢大家的支持

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😊

希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🙏

如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🌟

相关推荐
Swizard2 小时前
别让你的密钥在互联网上“裸奔”!用 python-dotenv 优雅管理你的敏感配置
python
无心水2 小时前
【Stable Diffusion 3.5 FP8】8、生产级保障:Stable Diffusion 3.5 FP8 伦理安全与问题排查
人工智能·python·安全·docker·stable diffusion·ai镜像开发·镜像实战开发
北极糊的狐2 小时前
若依报错:Request method ‘GET‘ not supported
状态模式
BD_Marathon2 小时前
设计模式的分类
设计模式
深蓝海拓2 小时前
PySide6从0开始学习的笔记(十八) MVC(Model-View-Controller)模式的图形渲染体系
笔记·python·qt·学习·pyqt
一招定胜负2 小时前
杂记:cv2.imshow显示中文乱码解决过程
python·opencv
唐叔在学习2 小时前
Pyinstaller进阶之构建管理大杀器-SPEC文件
后端·python·程序员
爱吃山竹的大肚肚2 小时前
在Java中,从List A中找出List B没有的数据(即求差集)
开发语言·windows·python
weixin_462446232 小时前
【原创实践】Python 将 Markdown 文件转换为 Word(docx)完整实现
开发语言·python·word