Roadmap › Design Patterns (GoF + Enterprise) in C# › Module: Structural Patterns
Lessons in this course (18)

7. Decorator

Intermediate★ Must knowLesson 7 of 18

Add behaviour to an object dynamically by wrapping it, without changing its class. Wrappers implement the same interface.

csharp
public interface IProductRepository { Task<Product?> GetAsync(int id); }

public class SqlProductRepository(AppDbContext db) : IProductRepository
{
    public Task<Product?> GetAsync(int id) => db.Products.FindAsync(id).AsTask();
}

public class CachedProductRepository(IProductRepository inner, IMemoryCache cache) : IProductRepository
{
    public Task<Product?> GetAsync(int id) =>
        cache.GetOrCreateAsync($"product:{id}", e => { e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5); return inner.GetAsync(id); });
}

// With Scrutor:
builder.Services.AddScoped<IProductRepository, SqlProductRepository>();
builder.Services.Decorate<IProductRepository, CachedProductRepository>();

.NET examples: Stream wrappers (BufferedStream, GZipStream, CryptoStream), DelegatingHandler in HttpClient.