State Pattern (Mẫu State)
Định nghĩa (Definition)
State cho phép object thay đổi hành vi khi internal state thay đổi. Object sẽ có vẻ như thay đổi class. Mỗi state được đóng gói trong class riêng, và context ủy quyền cho state object hiện tại.
Ví dụ Coffee Shop
Đơn cà phê có vòng đời: Created → Paid → Brewing → Ready → Collected. Ở mỗi state, đơn phản hồi khác nhau với cùng action — không thể pha đơn chưa thanh toán, không thể lấy đơn chưa sẵn sàng.
Cấu trúc (Structure)
State Interface
public interface IOrderState
{
void Pay(OrderContext order);
void StartBrewing(OrderContext order);
void FinishBrewing(OrderContext order);
void Collect(OrderContext order);
string GetName();
}
Context — Đơn hàng (The Order)
public class OrderContext
{
public string Customer { get; }
public string Coffee { get; }
public CupSize Size { get; }
private IOrderState _state;
public OrderContext(string customer, string coffee, CupSize size)
{
Customer = customer;
Coffee = coffee;
Size = size;
_state = new CreatedState();
}
public void TransitionTo(IOrderState state)
{
_state = state;
Console.WriteLine($" → State: {_state.GetName()}");
}
public void Pay() => _state.Pay(this);
public void StartBrewing() => _state.StartBrewing(this);
public void FinishBrewing() => _state.FinishBrewing(this);
public void Collect() => _state.Collect(this);
}
Concrete State
public class CreatedState : IOrderState
{
public void Pay(OrderContext order)
{
Console.WriteLine($" [Thanh toán] Thu tiền {order.Coffee}...");
order.TransitionTo(new PaidState());
}
public void StartBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể pha — đơn chưa thanh toán!");
public void FinishBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể hoàn thành — chưa bắt đầu pha!");
public void Collect(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể lấy — đơn chưa sẵn sàng!");
public string GetName() => "Created";
}
public class PaidState : IOrderState
{
public void Pay(OrderContext order) =>
Console.WriteLine(" ⚠️ Đã thanh toán rồi!");
public void StartBrewing(OrderContext order)
{
Console.WriteLine($" [Barista] Bắt đầu pha {order.Coffee}...");
order.TransitionTo(new BrewingState());
}
public void FinishBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể hoàn thành — chưa bắt đầu pha!");
public void Collect(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể lấy — đơn chưa sẵn sàng!");
public string GetName() => "Paid";
}
public class BrewingState : IOrderState
{
public void Pay(OrderContext order) =>
Console.WriteLine(" ⚠️ Đã thanh toán rồi!");
public void StartBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Đang pha rồi!");
public void FinishBrewing(OrderContext order)
{
Console.WriteLine($" [Barista] {order.Coffee} xong!");
order.TransitionTo(new ReadyState());
}
public void Collect(OrderContext order) =>
Console.WriteLine(" ⚠️ Không thể lấy — đang pha!");
public string GetName() => "Brewing";
}
public class ReadyState : IOrderState
{
public void Pay(OrderContext order) =>
Console.WriteLine(" ⚠️ Đã thanh toán rồi!");
public void StartBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Đã pha xong rồi!");
public void FinishBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Đã hoàn thành rồi!");
public void Collect(OrderContext order)
{
Console.WriteLine($" [Lấy hàng] {order.Customer} đã lấy {order.Coffee}! 🎉");
order.TransitionTo(new CollectedState());
}
public string GetName() => "Ready";
}
public class CollectedState : IOrderState
{
public void Pay(OrderContext order) =>
Console.WriteLine(" ⚠️ Đơn đã hoàn thành.");
public void StartBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Đơn đã hoàn thành.");
public void FinishBrewing(OrderContext order) =>
Console.WriteLine(" ⚠️ Đơn đã hoàn thành.");
public void Collect(OrderContext order) =>
Console.WriteLine(" ⚠️ Đơn đã được lấy rồi.");
public string GetName() => "Collected";
}
Client
var order = new OrderContext("Alice", "Latte", CupSize.Large);
order.StartBrewing(); // ⚠️ Không thể pha — đơn chưa thanh toán!
order.Pay(); // → State: Paid
order.StartBrewing(); // → State: Brewing
order.FinishBrewing(); // → State: Ready
order.Collect(); // → State: Collected
order.Pay(); // ⚠️ Đơn đã hoàn thành.
Mỗi state class xử lý cùng method khác nhau. OrderContext ủy quyền cho state hiện tại. Transition không hợp lệ được xử lý gracefully — không cần if (state == "Brewing") check.
State vs Strategy
| State | Strategy | |
|---|---|---|
| Ai thay đổi | Object tự thay đổi (internal transition) | Client (external injection) |
| Kiến thức | Client không biết về state | Client chọn strategy |
| Transition | State biết về state khác | Strategy không biết về nhau |
| Mục đích | Hành vi thay đổi theo state | Thuật toán thay đổi theo lựa chọn |
Sử dụng thực tế trong .NET (.NET Real-World Usage)
- ASP.NET Core
IAuthenticationHandler— hành vi khác nhau per auth state Task<T>lifecycle —Created → WaitingForActivation → Running → Completed/Faulted- Workflow engine (Elsa, Orleans grain) — xử lý theo state
- Game development — character state (idle, running, jumping, attacking)
Khi nào sử dụng (When to Use)
- Hành vi object phụ thuộc vào state và phải thay đổi lúc runtime
- Có khối
if/switchlớn kiểm tra state trong nhiều method - State transition phức tạp với nhiều rule
Khi nào KHÔNG sử dụng (When NOT to Use)
- Chỉ có 2 state — boolean flag đơn giản là đủ
- Logic state đơn giản —
enum+switchđơn giản hơn - State không có sự khác biệt hành vi phức tạp
Điểm chính (Key Takeaways)
- State đóng gói hành vi mỗi state trong class riêng
- Context ủy quyền cho state object hiện tại
- State transition diễn ra bên trong — context gọi
TransitionTo() - Loại bỏ khối điều kiện lớn — mỗi state class xử lý logic riêng
Câu hỏi phỏng vấn (Interview Questions)
Q: State khác finite state machine (FSM) như thế nào?
State pattern chính là implement FSM trong OOP. Mỗi state class đại diện một state, và transition là method call. FSM truyền thống dùng state table hoặc switch — State pattern dùng polymorphism.
Q: State nên là singleton hay instance mới? Nếu state không có state nội bộ (không instance field), có thể là singleton — chia sẻ cho tất cả context. Nếu state giữ data (như timer), tạo instance mới per context.
Chủ đề liên quan (Related Topics)
- Strategy — cấu trúc tương tự, client-driven (không state-driven)
- Template Method — thuật toán cố định với bước thay đổi
- Command — có thể trigger state transition