Patterns quản lý hành vi và tương tác giữa objects: Strategy, Observer, Mediator, Chain of Responsibility, Specification.
Strategy - Thay Đổi Algorithm Runtime
csharppublic interface IDiscountStrategy
{
bool CanApply(Order order);
decimal Calculate(Order order);
}
public class VipDiscount : IDiscountStrategy
{
public bool CanApply(Order order) => order.Customer.IsVip;
public decimal Calculate(Order order) => order.Total * 0.20m;
}
public class SeasonalDiscount : IDiscountStrategy
{
public bool CanApply(Order order) => DateTime.UtcNow.Month == 12; // Tháng 12
public decimal Calculate(Order order) => order.Total * 0.15m;
}
// Context selects strategy
public class PricingService(IEnumerable<IDiscountStrategy> strategies)
{
public decimal GetBestDiscount(Order order) =>
strategies.Where(s => s.CanApply(order)).Max(s => s.Calculate(order));
}
// DI: AddScoped<IDiscountStrategy, VipDiscount>()
// AddScoped<IDiscountStrategy, SeasonalDiscount>()
Dùng khi: Nhiều algorithms cho cùng 1 bài toán, switch theo config/runtime.
Dự án: Pricing, tax, shipping rate, notification channel, export format.
Observer - Pub/Sub In-Process
csharp// MediatR INotification = Observer pattern!
public record OrderCreatedEvent(Guid OrderId) : INotification;
public class SendEmailHandler : INotificationHandler<OrderCreatedEvent>
{
public Task Handle(OrderCreatedEvent e, CancellationToken ct) { /* send email */ return Task.CompletedTask; }
}
public class UpdateInventoryHandler : INotificationHandler<OrderCreatedEvent>
{
public Task Handle(OrderCreatedEvent e, CancellationToken ct) { /* decrement stock */ return Task.CompletedTask; }
}
// Publisher: await _mediator.Publish(new OrderCreatedEvent(order.Id));
// → Tất cả handlers được gọi tự động
Dùng khi: 1 event → nhiều side effects, decouple producer khỏi consumers.
.NET Built-in: MediatR Notifications, Event handlers, IObservable<T>.
Mediator - Trung Gian Điều Phối
csharp// Mediator pattern = trung tâm routing
// Controller không biết Handler nào xử lý
// Handler không biết ai gọi nó
// MediatR chính là implementation Mediator pattern trong .NET
// Thêm Pipeline Behaviors = Chain of Responsibility quanh Mediator
// Request → [Validation] → [Logging] → [Transaction] → [Handler]
Khác biệt Mediator vs Observer:
- Observer: 1 event → N handlers (fan-out, broadcast)
- Mediator: 1 request → 1 handler (routing, single handler)
Chain of Responsibility
csharp// ASP.NET Middleware Pipeline = Chain of Responsibility!
// Mỗi middleware quyết định: forward (next()) hoặc short-circuit (return)
// Custom validation chain
public abstract class OrderValidator
{
private OrderValidator? _next;
public OrderValidator SetNext(OrderValidator next)
{
_next = next;
return next;
}
public virtual ValidationResult Validate(Order order)
{
if (_next is not null) return _next.Validate(order);
return ValidationResult.Success;
}
}
public class StockValidator : OrderValidator
{
public override ValidationResult Validate(Order order)
{
if (order.Quantity > MaxStock)
return ValidationResult.Fail("Insufficient stock");
return base.Validate(order); // Pass to next
}
}
// Usage: stockValidator.SetNext(creditValidator).SetNext(fraudValidator);
Dùng khi: Pipeline xử lý tuần tự, mỗi step có thể dừng hoặc chuyển tiếp.
.NET Built-in: Middleware pipeline, DelegatingHandler (HttpClient), MediatR Pipeline Behaviors.
Specification - Encapsulate Business Rules
csharp// Specification encapsulates query logic thành reusable objects
public abstract class Specification<T>
{
public abstract Expression<Func<T, bool>> ToExpression();
public Specification<T> And(Specification<T> other)
=> new AndSpecification<T>(this, other);
public Specification<T> Or(Specification<T> other)
=> new OrSpecification<T>(this, other);
}
// Concrete specifications
public class ActiveOrderSpec : Specification<Order>
{
public override Expression<Func<Order, bool>> ToExpression()
=> order => order.Status == OrderStatus.Active;
}
public class MinAmountSpec(decimal min) : Specification<Order>
{
public override Expression<Func<Order, bool>> ToExpression()
=> order => order.Total >= min;
}
// Usage: composable business rules!
var spec = new ActiveOrderSpec().And(new MinAmountSpec(100));
var orders = await db.Orders.Where(spec.ToExpression()).ToListAsync();
// → SELECT * FROM Orders WHERE Status = 'Active' AND Total >= 100
Dùng khi: Business rules phức tạp, cần compose (AND/OR), cần reuse across queries.
Library: Ardalis.Specification (NuGet) - tích hợp sẵn với EF Core.