Patterns tổ chức quan hệ giữa các objects: Adapter, Decorator, Facade, Proxy, Composite.
Adapter - Chuyển Đổi Interface
csharp// Bọc third-party SDK cho phù hợp với interface của bạn
public interface IPaymentGateway
{
Task<PaymentResult> ChargeAsync(decimal amount, string currency);
}
// Stripe SDK dùng API khác hoàn toàn
public class StripeAdapter(StripeClient stripe) : IPaymentGateway
{
public async Task<PaymentResult> ChargeAsync(decimal amount, string currency)
{
// Translate sang Stripe API
var options = new PaymentIntentCreateOptions
{
Amount = (long)(amount * 100), // Stripe dùng cents!
Currency = currency.ToLower()
};
var intent = await stripe.PaymentIntents.CreateAsync(options);
return new PaymentResult(intent.Id, intent.Status == "succeeded");
}
}
Dùng khi: Integrate third-party (Stripe, SendGrid, Twilio) mà không couple app code.
Dự án: Mọi project có external integration.
Decorator - Thêm Behavior Không Sửa Code Gốc
csharp// Base implementation
public class OrderRepository(AppDbContext db) : IOrderRepository
{
public async Task<Order?> GetByIdAsync(Guid id)
=> await db.Orders.FindAsync(id);
}
// Decorator 1: Caching (wraps original)
public class CachedOrderRepository(IOrderRepository inner, IMemoryCache cache) : IOrderRepository
{
public async Task<Order?> GetByIdAsync(Guid id)
{
var key = $"order:{id}";
if (cache.TryGetValue(key, out Order? cached)) return cached;
var order = await inner.GetByIdAsync(id); // Gọi INNER
if (order is not null) cache.Set(key, order, TimeSpan.FromMinutes(5));
return order;
}
}
// Decorator 2: Logging (wraps cached)
public class LoggedOrderRepository(IOrderRepository inner, ILogger<LoggedOrderRepository> logger) : IOrderRepository
{
public async Task<Order?> GetByIdAsync(Guid id)
{
logger.LogInformation("Getting order {OrderId}", id);
var order = await inner.GetByIdAsync(id);
logger.LogInformation("Order {OrderId} {Result}", id, order is null ? "not found" : "found");
return order;
}
}
// Registration (composition)
builder.Services.AddScoped<OrderRepository>();
builder.Services.AddScoped<IOrderRepository>(sp =>
new LoggedOrderRepository(
new CachedOrderRepository(
sp.GetRequiredService<OrderRepository>(),
sp.GetRequiredService<IMemoryCache>()),
sp.GetRequiredService<ILogger<LoggedOrderRepository>>()));
// .NET Built-in: HttpClient DelegatingHandler chính là Decorator pattern!
Dùng khi: Layer thêm caching, logging, auth, retry MÀ KHÔNG sửa class gốc.
Facade - Đơn Giản Hóa Hệ Thống Phức Tạp
csharp// Client không cần biết 4 services bên dưới
public class OrderFacade(
IInventoryService inventory,
IPaymentService payment,
IShippingService shipping,
INotificationService notification)
{
public async Task<OrderResult> PlaceOrderAsync(OrderRequest req)
{
var stock = await inventory.ReserveAsync(req.ProductId, req.Quantity);
if (!stock.IsAvailable) return OrderResult.Fail("Out of stock");
var payment = await payment.ChargeAsync(req.Total, req.PaymentMethod);
if (!payment.IsSuccess)
{
await inventory.ReleaseAsync(stock.ReservationId);
return OrderResult.Fail("Payment failed");
}
await shipping.ScheduleAsync(req.Address);
await notification.SendConfirmationAsync(req.Email);
return OrderResult.Success();
}
}
Dùng khi: Orchestrate nhiều services phức tạp, expose API đơn giản cho caller.
Proxy - Kiểm Soát Truy Cập
csharp// Lazy Loading Proxy
public class LazyProductService(IServiceScopeFactory scopeFactory) : IProductService
{
public async Task<Product?> GetByIdAsync(Guid id)
{
// Only create scope/DbContext khi thực sự cần
using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await db.Products.FindAsync(id);
}
}
Dùng khi: Lazy loading, access control, remote proxy (gRPC client), caching proxy.
Composite - Xử Lý Cây Phân Cấp
csharp// Permission tree, Menu tree, Organization tree
public interface IMenuComponent
{
string Name { get; }
decimal GetTotalPrice();
}
public class MenuItem(string name, decimal price) : IMenuComponent
{
public string Name => name;
public decimal GetTotalPrice() => price;
}
public class MenuCategory(string name) : IMenuComponent
{
private readonly List<IMenuComponent> _children = [];
public string Name => name;
public void Add(IMenuComponent item) => _children.Add(item);
public decimal GetTotalPrice() => _children.Sum(c => c.GetTotalPrice()); // Recursive!
}
Dùng khi: File systems, menus, permission trees, organization hierarchies.