Decorator Pattern: Enhancing Objects at Runtime
Decorator Pattern: Enhancing Objects at Runtime
The Decorator Pattern is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.
๐๏ธ The Problem
Suppose you have a Repository class that fetches data from a database. Now, you want to add Logging and Caching to it. One way is to modify the existing Repository class, but that violates the Single Responsibility Principle. Another way is to use inheritance, but that can lead to an explosion of subclasses (e.g., LoggingRepository, CachingRepository, LoggingAndCachingRepository).
๐ The .NET Implementation
The Decorator pattern is ideal for adding โcross-cutting concernsโ like logging, caching, and validation.
1. The Base Interface
public interface IDataService
{
string GetData(int id);
}2. The Concrete Component
public class DataService : IDataService
{
public string GetData(int id)
{
// Simulate a database call
Console.WriteLine("Fetching data from the Database...");
return $"Data for ID: {id}";
}
}3. The Base Decorator (Wrapper)
public class LoggingDecorator : IDataService
{
private readonly IDataService _innerService;
public LoggingDecorator(IDataService innerService)
{
_innerService = innerService;
}
public string GetData(int id)
{
Console.WriteLine($"[LOG]: Starting to fetch data for ID: {id}");
var result = _innerService.GetData(id);
Console.WriteLine($"[LOG]: Finished fetching data for ID: {id}");
return result;
}
}๐ ๏ธ Real-World Usage (Client)
In .NET Core, you can chain these decorators using Dependency Injection or simple composition.
// Wrap the base service with a logger
IDataService service = new DataService();
IDataService loggedService = new LoggingDecorator(service);
// This will now log AND fetch from the DB
var result = loggedService.GetData(123);๐ก Why use Decorator?
- SRP (Single Responsibility Principle): Keep the core logic separate from cross-cutting concerns (logging, caching).
- Flexibility: You can combine decorators in any order. For example,
new CachingDecorator(new LoggingDecorator(new DataService())). - Alternatives: In .NET, you can also use Middlewares or Action Filters to achieve similar results, but the Decorator pattern is more general and works at any layer.