Decorator Pattern in C#
The problem
Imagine you were using a software development kit (SDK) to fetch some secrets from a cloud provider. The SDK class is sealed (you can’t sub-class it) and it has a rate-limit that is lower than what you need. You’d like to add a cache to it but you’d like to avoid having to create a new interface and update all the references in your app, what can you do?
The decorator pattern is an alternative to sub-classing.
Wrap it up
The pattern also goes by the name of “wrapper pattern” since we’re essentially going to wrap our trouble class in another implementation. What makes this pattern useful, is it allows us to side-step sealed class restrictions by simply swallowing the original class with our own class.
But first, let’s look at that problem class code below:
namespace SomeSdk;
public interface ISecretsService
{
string GetSecret(string secretName);
}
public sealed class SecretsService : ISecretsService
{
public string GetSecret(string secretName)
{
return "a secret";
}
}This class is clearly closed for modification not only because we don’t have the ability to edit it (in a separate compiled DLL) but it’s also marked sealed.
To remedy this, we can simply create a new wrapper class that implements the same interface as the SDK class.
using System.Collections.Concurrent;
using SomeSdk;
namespace DotnetDev.DecoratorPattern;
//implements the same interface
public class CachedSecretsService : ISecretsService
{
private readonly ISecretsService _sealedSecretsService;
private static readonly ConcurrentDictionary<string, string> Cache = new ();
public CachedSecretsService(ISecretsService sealedSecretsService)
{
_sealedSecretsService = sealedSecretsService;
}
//implements the same method
public string GetSecret(string secretName)
{
if (Cache.TryGetValue(secretName, out var cachedSecret))
return cachedSecret;
//calls the original one, often called the "inner" implementation
var secret = _sealedSecretsService.GetSecret(secretName);
Cache.TryAdd(secretName, secret);
return secret;
}
}The thing to notice is that we take another instance of ISecretsService into our new ISecretsService implementation. What this allows us to do is call code just before or just after the original GetSecret(…) method.
Dependency Injection
In order to make this work, we need to register both the original SDK implementation and our new cached version. Let’s take a look below:
using DotnetDev.DecoratorPattern;
using Microsoft.Extensions.DependencyInjection;
using SomeSdk;
Console.WriteLine("Hello, World!");
var services = new ServiceCollection();
//register the SDK one
services.AddTransient<SecretsService>();
//register the new one and resolve the original internally
services.AddTransient<ISecretsService>(x => new CachedSecretsService(x.GetRequiredService<SecretsService>()));
var provider = services.BuildServiceProvider();
//this will resolve our cached one but take the SDK one as a dependency
var cachedSecretsService = provider.GetService<ISecretsService>();
var secret = cachedSecretsService?.GetSecret("some name");
Console.WriteLine(secret);What if it weren’t sealed?
If the SDK class weren’t sealed, we could extend the original and override the GetSecret(…) so long as it were marked virtual. If the original author did not mark it as such, the decorator pattern has got your back in that scenario as well.
So when might you use this?
This pattern is often used for caching, logging, authorization and other cross-cutting concerns. Another good use-case is to firewall legacy code that might not be using proper abstractions or dependency injection. By wrapping the legacy code with a class that does use an abstraction, some level of unit testing can be put into place.
A full example can be found here.
Now get out there and decorate some code!

