EngineeringSenior DeveloperArchitecture and Patterns

08 - Behavioral Design Patterns

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

csharp
public 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.