TL;DR

State Pattern theo kiểu Gang of Four — một abstract base, một class cho mỗi state, context tự delegate — đúng về lý thuyết nhưng tốn kém trong thực tế. Một order có 15 state nghĩa là 16 class và hàng trăm dòng delegation. Bản viết lại mà video gốc đề xuất gom tất cả về một Enum + một bảng transition + một engine generic dùng chung. Cùng đảm bảo, ít hơn 80% dòng code, và điều quan trọng nhất: trạng thái bất hợp lệ trở nên không thể biểu diễn.

Cách tiếp cận mới: state là dữ liệu, không phải class

Ý tưởng cốt lõi đơn giản đến mức gây khó chịu với người học GoF lần đầu: state không cần là class. Trong Python hiện đại, ba công cụ đã có sẵn đủ để thay thế toàn bộ class hierarchy:

  • Enum — mỗi state là một member, không phải một file.
  • Decorator@transition(source=…, dest=…) khai báo chuyển tiếp trên method, thay cho việc viết self.state = PaidState() rải khắp nơi.
  • Generics (PEP 695, Python 3.12) — engine StateMachine[S, Ctx] dùng lại cho mọi FSM trong codebase, vẫn giữ type-safety.

Kết quả là transition diagram nằm trên một màn hình dưới dạng dữ liệu, thay vì scatter qua 15 file. Code review nhanh hơn, onboarding nhanh hơn, và static checker có đủ thông tin để cảnh báo transition lạc đường.

Vì sao đáng quan tâm

Bug kinh điển của domain object sống lâu — order, document, user, job — thường không phải là logic sai ở một bước, mà là object rơi vào tổ hợp state không được phép tồn tại. Gốc rễ thường là boolean blindness: dùng is_paid, is_shipped, is_cancelled song song. N cờ boolean sinh ra 2^N tổ hợp, phần lớn là illegal.

Một hệ thống có thể cùng lúc ở hai trạng thái mâu thuẫn thì không phải là hệ thống — đó là quả bom nổ chậm.

Một field Enum duy nhất thay thế toàn bộ cờ boolean buộc hệ thống về đúng N state hợp lệ. Thêm decorator transition và engine generic, bạn có guarantee ở tầng type-checker chứ không phải hy vọng.

Technical facts

So sánh cụ thể giữa bản classic và bản data-driven cho một workflow 10 state:

Chỉ sốGoF State PatternData-driven FSM
Số class~11 (1 abstract + 10 concrete)1 Enum + 1 machine definition
Lines of code250–40080–120
Thêm state mớiNew class + sửa context + sửa siblings+1 Enum member, +1 dòng trong table
Transition saiRuntime error nếu nhớ raiseEngine chặn, test thấy, diagram lộ ra
Type-checker hỗ trợHạn chếĐầy đủ qua generics
Diagram nằm ở đâuRải khắp N fileMột bảng đọc được như dữ liệu

Thư viện production phổ biến nhất là pytransitions/transitions — 5k+ sao, hỗ trợ hierarchical, nested, async, wildcard source '*', tự sinh trigger method. python-statemachine 3.x đi theo hướng DSL class-body với @event decorator và Enum first-class. Nếu không muốn thêm dependency, một engine tự viết cỡ 60 dòng là đủ cho hầu hết dự án.

Pattern nào chọn khi nào

Ba lựa chọn thường gặp — và thứ ba là anti-pattern:

  • GoF State Pattern — vẫn hợp lý khi mỗi state có logic riêng rất phức tạp, team nhỏ ưu tiên explicit, hoặc dùng cho mục đích dạy học.
  • Data-driven FSM (Enum + decorator + engine) — mặc định cho production: workflow dài, nhiều state, cần audit, cần type-safety.
  • If/elif lồng nhau — never. Tăng state là tăng nguy cơ logic sai theo cấp số nhân, và không test nào đủ bao phủ.

Use cases tiêu biểu

  • Order & checkout — draft → pending → paid → shipped → delivered / refunded / cancelled.
  • Editorial workflow — draft → in review → approved → published → archived.
  • Job runner / CI-CD — queued → running → succeeded / failed, retry loop rõ ràng.
  • KYC & document signing — invited → submitted → under review → approved / rejected.
  • Game AI — idle → patrolling → chasing → attacking → fleeing, lãnh địa kinh điển của FSM.
  • Firmware IoT — sleeping → booting → connected → streaming → updating.

Giới hạn & trade-offs

  • Không pickle được khi decorator tự attach method động — ảnh hưởng multiprocessing, celery worker.
  • Debugger khó step-in vào method auto-generated. Khi bug, phải đọc transition table để hiểu đường đi.
  • Side effects và persistence (commit state sang Postgres, gọi webhook) vẫn phải wire bằng tay. Engine không biết về DB của bạn.
  • Overkill cho toggle hai trạng thái (on/off, open/closed) — boolean là đủ.
  • Đường cong học: engineer quen GoF hay phản đối vì "không giống sách". Câu trả lời là: sách viết trước khi Python có Enum, generics và decorator.
  • Chi phí: transitionspython-statemachine đều open-source miễn phí (MIT/LGPL). Engine tự viết ~60 dòng, zero dependency.

What's next

Lộ trình Python đang đẩy pattern này đi xa hơn. PEP 695 (Python 3.12) làm generics engine gọn hẳn. Python 3.13 ổn định StrEnum và cải thiện __repr__. Python 3.14 giúp match/case cảnh báo exhaustiveness thực dụng ở static checker — nghĩa là FSM engine có thể warn về state chưa handle ngay khi lint.

Bước lớn kế tiếp là type-system-enforced transitions: phantom type, Literal narrowing để một transition bất hợp lệ trở thành gạch đỏ trong editor thay vì runtime exception. Khi tới đó, State Pattern kinh điển sẽ chỉ còn là bài đọc lịch sử.

Nguồn: The State Pattern in Python — I Like How This Turned Out (YouTube), DEV — Python State Machines 2026, pytransitions/transitions, ArjanCodes — Python 3.12 Generics.