Chuyển tới nội dung chính

Chain of Responsibility Pattern (Mẫu Chain of Responsibility)

Định nghĩa (Definition)

Chain of Responsibility tránh coupling sender của request với receiver bằng cách cho nhiều hơn một object cơ hội xử lý request. Các object nhận được xâu chuỗi, và request đi dọc theo chuỗi cho đến khi một object xử lý.

Ví dụ Coffee Shop

Trước khi đơn được chấp nhận, nó đi qua pipeline kiểm tra: kiểm tra tồn kho (Stock Check) → kiểm tra size (Size Availability) → giới hạn ngày (Daily Limit) → xác thực thanh toán (Payment Auth). Mỗi handler kiểm tra một mối quan tâm và hoặc chuyển request cho handler tiếp theo hoặc từ chối.

Cấu trúc (Structure)

Base Handler

public record OrderRequest(string Customer, string Coffee, CupSize Size, decimal Price);

public interface IOrderHandler
{
IOrderHandler SetNext(IOrderHandler handler);
string Handle(OrderRequest order);
}

public abstract class OrderHandlerBase : IOrderHandler
{
private IOrderHandler? _next;

public IOrderHandler SetNext(IOrderHandler handler)
{
_next = handler;
return handler; // Cho phép fluent chaining
}

public virtual string Handle(OrderRequest order) =>
_next?.Handle(order) ?? "✅ Đơn được duyệt — tất cả kiểm tra đã qua!";
}

Concrete Handler

public class StockCheckHandler : OrderHandlerBase
{
private readonly HashSet<string> _available = new() { "Latte", "Espresso", "Cappuccino" };

public override string Handle(OrderRequest order)
{
if (!_available.Contains(order.Coffee))
return $"❌ Từ chối: {order.Coffee} đã hết hàng.";
Console.WriteLine($" [Tồn kho] ✓ {order.Coffee} còn hàng");
return base.Handle(order);
}
}

public class SizeCheckHandler : OrderHandlerBase
{
public override string Handle(OrderRequest order)
{
if (order.Coffee == "Espresso" && order.Size == CupSize.Large)
return $"❌ Từ chối: Espresso chỉ có đến Medium.";
Console.WriteLine($" [Size] ✓ {order.Size} {order.Coffee} hợp lệ");
return base.Handle(order);
}
}

Client — Xây dựng chuỗi (Building the Chain)

var stock = new StockCheckHandler();
var size = new SizeCheckHandler();
var limit = new DailyLimitHandler();
var payment = new PaymentHandler();

stock.SetNext(size).SetNext(limit).SetNext(payment);

var result = stock.Handle(new OrderRequest("Alice", "Latte", CupSize.Large, 5.00m));
// [Tồn kho] ✓ Latte còn hàng
// [Size] ✓ Large Latte hợp lệ
// [Giới hạn] ✓ Alice đã đặt 1/5 hôm nay
// [Thanh toán] ✓ $5.00 trong giới hạn
// ✅ Đơn được duyệt!
mẹo

Mỗi handler làm một việc (SRP). Chuỗi được xây bởi client — có thể sắp xếp lại, thêm, hoặc xóa handler mà không sửa code handler. Sender chỉ biết handler đầu tiên trong chuỗi.

.NET Real-World Usage

  • ASP.NET Core Middleware Pipeline — mỗi middleware xử lý request hoặc chuyển cho next
  • DelegatingHandler chain trong HttpClient
  • Exception handling middleware — thử handler theo thứ tự cho đến khi bắt được
  • Authorization policy — kiểm tra policy theo thứ tự

Khi nào sử dụng (When to Use)

  • Nhiều hơn một object có thể xử lý request, và handler chưa biết trước
  • Muốn gửi request đến nhiều object mà không chỉ định receiver
  • Tập handler cần configurable lúc runtime

Khi nào KHÔNG sử dụng (When NOT to Use)

  • Chỉ có một handler — không cần chuỗi
  • Thứ tự handler không quan trọng — list validator đơn giản là đủ
  • Mọi handler phải xử lý request — dùng Decorator

Điểm chính (Key Takeaways)

  • Chain of Responsibility tách biệt sender khỏi receiver
  • Mỗi handler kiểm tra một điều kiện và chuyển tiếp hoặc từ chối
  • Chuỗi configurable — thêm, xóa, hoặc sắp xếp lại handler
  • Pattern short-circuit: khi handler từ chối, chuỗi dừng

Câu hỏi phỏng vấn (Interview Questions)

Q: ASP.NET Core middleware liên quan đến Chain of Responsibility như thế nào? ASP.NET Core middleware chính là Chain of Responsibility. Mỗi middleware nhận HttpContext, làm việc, và gọi next() để chuyển cho middleware tiếp theo. Delegate next là link trong chuỗi. Middleware có thể short-circuit bằng không gọi next().

Q: Nhiều handler trong chuỗi có thể cùng xử lý request không? Có — pattern kinh điển nói "một handler xử lý", nhưng biến thể cho mọi handler xử lý (như ASP.NET middleware). Điểm mấu chốt là mỗi handler quyết định có chuyển tiếp request.

  • Decorator — mọi decorator xử lý, chain short-circuit
  • Mediator — routing tập trung vs chuỗi phi tập trung
  • Middleware — ASP.NET Core implement pattern này