OOP phù hợp cho game vì domain modeling

OOP và các paradigm khác (procedural, functional, declarative) không tốt hơn nhau theo cách trừu tượng — paradigm nào phù hợp phụ thuộc vào vocabulary của bài toán. Một paradigm có vocabulary trùng với domain sẽ làm code dễ nghĩ, dễ viết, dễ đọc lại. Game là ví dụ điển hình của bài toán map 1-1 với vocabulary của OOP.

Vocabulary của game trùng vocabulary của OOP

Game cấu thành từ các thực thể (entity) tách biệt, mỗi thực thể có trạng thái riêng (state) và hành vi riêng (behavior). Snake giữ vị trí, hướng và độ dài; Food giữ vị trí và loại; Wall là tập rect tĩnh; Player có score và HP. Vocabulary này khớp gần như 1-1 với vocabulary OOP: class = loại entity, instance = entity cụ thể, field = state, method = behavior.

Hệ quả: dev tư duy ở tầng domain (“snake ăn food thì dài ra”), code ở tầng OOP cũng đọc tự nhiên như câu đó: snake.eat(food); snake.grow();. Khoảng cách giữa cách dev mô tả game trong đầu và cách code biểu đạt nó là gần như không. Đây gọi là low impedance mismatch giữa domain model và code model.

Procedural style cho cùng game phải viết: eat_food(snake_state, food_state); grow_snake(snake_state); — phải pass state object qua hàm, khó nhìn ra “ai sở hữu cái gì”. Functional style phải viết snake' = eat(snake, food) — phải trả về state mới mỗi lần, không hợp với loop game tick mà entity được mutate liên tục.

Khi nào paradigm khác phù hợp hơn

Tư duy quan trọng: chọn paradigm theo bản chất bài toán, không theo định kiến. Bài toán có bản chất khác sẽ chọn paradigm khác:

Game thì là entity-driven với state mutation liên tục — vocabulary OOP. REST thì stateless — vocabulary procedural. Cùng một dev có thể (và nên) chọn khác paradigm cho khác bài toán.

Trade-off với scope

OOP không miễn phí: nhiều file, nhiều class, nhiều dependency để hiểu trước khi sửa được. Game prototype nhỏ (vài trăm dòng) có thể viết 1 file global vars vẫn rõ và sửa nhanh hơn. Game production (vài nghìn dòng, nhiều state, nhiều entity) thì OOP trả về investment qua khả năng mở rộng.

Quy tắc thực dụng: simple-first. Bắt đầu bằng cấu trúc đơn giản nhất đủ work, refactor sang OOP khi scope đòi hỏi. Đây là cùng tư duy với Đơn giản hơn linh hoạt trong thiết kế API — đơn giản trước, phức tạp khi có nhu cầu thật.

Trải nghiệm cá nhân

Insight này được internalize qua 3 game SDL2 thời 2016–2017 (repo sdl_game):

Lý do nhận ra OOP phù hợp với game: tư duy của người viết dễ tổ chức và suy luận từ các object trong đời thật hơn là lập trình hàm. Bây giờ nếu prototype game nhỏ vẫn chọn 1 file đơn giản, game lớn mới chọn OOP nghiêm túc — đây là meta-skill chọn cấu trúc theo scope mà thời 2016 chưa có (lúc đó cứng nhắc một stack). Chi tiết trong transcript phỏng vấn.

Cập nhật: 2026-05-29