Enhancing Functionality with the Decorator Pattern in .NET
- Get link
- X
- Other Apps
Introduction
The Decorator Pattern is a structural design pattern that allows you to dynamically add new functionality to an object without changing its existing behaviour. This pattern is highly effective in situations where you need to extend the functionality of an object without making changes to its code or using inheritance. In this blog post, we will discuss the decorator pattern in .NET using a real-world example.
Consider a scenario where you have a list of players and you need to fetch the list from a service. The service might take a considerable amount of time to return the list of players. You may want to add caching or logging functionality to the service, but you don't want to modify the existing code. This is where the decorator pattern comes into play.
Example
Define the Interface
First, we define the interface IPlayersService with a single method, GetPlayersList():
public interface IPlayersService
{
IEnumerable GetPlayersList();
}
Implement the Interface
Next, we create a concrete implementation of the IPlayersService interface, called PlayersService, which fetches the list of players and simulates a delay using Thread.Sleep(4000):
public class PlayersService : IPlayersService
{
public IEnumerable GetPlayersList()
{
Thread.Sleep(4000);
return new List()
{
new Player(){ Id=1, Name="Syed Haider"},
new Player(){ Id=2, Name="Ali Haider"},
new Player(){ Id=3, Name="Muhammad Haider"},
new Player(){ Id=4, Name="Ali Mustafa"},
new Player(){ Id=5, Name="Musa Kazim"}
};
}
}
Create the Decorators
a. Caching Decorator
public class PlayersServiceCachingDecorator : IPlayersService
{
private readonly IPlayersService _playersService;
private readonly IMemoryCache _memoryCache;
private const string GetPlayersListCacheKey = "players.list";
public PlayersServiceCachingDecorator(IPlayersService playersService, IMemoryCache memoryCache)
{
_playersService = playersService;
_memoryCache = memoryCache;
}
public IEnumerable GetPlayersList()
{
IEnumerable? players = null;
if (_memoryCache.TryGetValue(GetPlayersListCacheKey, out players)) return players;
players = _playersService.GetPlayersList();
var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(1));
_memoryCache.Set(GetPlayersListCacheKey, players, cacheEntryOptions);
return players;
}
}
b. Logging Decorator
public class PlayersServiceLoggingDecorator : IPlayersService
{
private readonly IPlayersService _playersService;
private readonly ILogger _logger;
public PlayersServiceLoggingDecorator
(
IPlayersService playersService,
ILogger logger
)
{
_playersService = playersService
_logger = logger;
}
public IEnumerable GetPlayersList()
{
_logger.LogInformation("Starting to fetch data..");
var stopWatch = Stopwatch.StartNew();
var players = _playersService.GetPlayersList();
foreach (var player in players)
{
_logger.LogInformation("Player: " + player.Id + ", Name: " + player.Name);
}
stopWatch.Stop();
var elapsedTime = stopWatch.ElapsedMilliseconds;
_logger.LogInformation($"Finished fetching data in {elapsedTime} milliseconds");
return players;
}
}
Configure the Decorators
builder.Services.AddMemoryCache();
builder.Services.AddScoped();
builder.Services.Decorate<IPlayersService, PlayersServiceCachingDecorator>();
builder.Services.Decorate<IPlayersService, PlayersServiceLoggingDecorator>();
Dynamic Configuration of Decorators
{
"EnableCaching": "true",
"EnableLogging": "true"
}
if (Convert.ToBoolean(builder.Configuration["EnableCaching"]))
{
builder.Services.Decorate<IPlayersService, PlayersServiceCachingDecorator>();
}
if (Convert.ToBoolean(builder.Configuration["EnableLogging"]))
{
builder.Services.Decorate<IPlayersService, PlayersServiceLoggingDecorator>();
}
Introducing Scrutor
Install-Package Scrutor
builder.Services.Decorate();
Conclusion
- Get link
- X
- Other Apps
Comments
Source Code can found here: https://github.com/SyedShahbaz/DecoratorPattern
ReplyDelete