Decorator Pattern en .NET

Découvrez comment le Decorator Pattern peut rendre votre code .NET plus flexible et maintenable. Un guide pratique pour ajouter dynamiquement des fonctionnalités à vos classes sans modifier leur st...

Olivier Dupuy
23 juillet 2025

9

Vues

0

Commentaires

2

Min de lecture

Le Decorator Pattern est l'un des patrons de conception les plus élégants et flexibles de la programmation orientée objet. Dans l'écosystème .NET, il permet d'étendre dynamiquement les fonctionnalités d'objets sans altérer leur structure de base. Ce pattern est particulièrement pertinent dans le développement d'applications modernes avec .NET 8, où la flexibilité et la maintenabilité sont essentielles.

Comprendre le Decorator Pattern

Le Decorator Pattern fait partie des patterns structurels du GOF (Gang of Four). Il permet d'ajouter des comportements à des objets de manière dynamique en les enveloppant dans des objets de classes décorateurs. Cette approche offre une alternative flexible à l'héritage pour étendre les fonctionnalités.

Structure de base


// Interface de base
public interface IComponent
{
    string Operation();
}

// Composant concret public class ConcreteComponent : IComponent { public string Operation() { return "ConcreteComponent"; } }

// Décorateur abstrait public abstract class Decorator : IComponent { protected IComponent _component;

public Decorator(IComponent component) { _component = component; }

public virtual string Operation() { return _component.Operation(); } }

// Décorateur concret public class ConcreteDecorator : Decorator { public ConcreteDecorator(IComponent component) : base(component) { }

public override string Operation() { return $"ConcreteDecorator({base.Operation()})"; } }

Implémentation pratique en .NET

Voici un exemple concret utilisant le Decorator Pattern dans un contexte de logging d'API :


public interface IApiService
{
    Task GetDataAsync(string endpoint);
}

public class ApiService : IApiService { private readonly HttpClient _httpClient;

public ApiService(HttpClient httpClient) { _httpClient = httpClient; }

public async Task GetDataAsync(string endpoint) { return await _httpClient.GetStringAsync(endpoint); } }

public class LoggingDecorator : IApiService { private readonly IApiService _apiService; private readonly ILogger _logger;

public LoggingDecorator(IApiService apiService, ILogger logger) { _apiService = apiService; _logger = logger; }

public async Task GetDataAsync(string endpoint) { try { _logger.LogInformation($"Calling endpoint: {endpoint}"); var result = await _apiService.GetDataAsync(endpoint); _logger.LogInformation($"Success for endpoint: {endpoint}"); return result; } catch (Exception ex) { _logger.LogError(ex, $"Error calling endpoint: {endpoint}"); throw; } } }

Configuration dans ASP.NET Core

L'intégration du pattern dans ASP.NET Core se fait généralement via l'injection de dépendances :


public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
    services.AddScoped();
    services.Decorate();
}

Cas d'usage avancés

Caching Decorator


public class CachingDecorator : IApiService
{
    private readonly IApiService _apiService;
    private readonly IMemoryCache _cache;

public CachingDecorator(IApiService apiService, IMemoryCache cache) { _apiService = apiService; _cache = cache; }

public async Task GetDataAsync(string endpoint) { var cacheKey = $"api_data_{endpoint}"; if (_cache.TryGetValue(cacheKey, out string cachedResult)) { return cachedResult; }

var result = await _apiService.GetDataAsync(endpoint); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)); _cache.Set(cacheKey, result, cacheEntryOptions); return result; } }

Tests unitaires

Voici un exemple de tests unitaires utilisant xUnit :


public class ApiServiceDecoratorTests
{
    [Fact]
    public async Task LoggingDecorator_ShouldLogApiCalls()
    {
        // Arrange
        var mockLogger = new Mock>();
        var mockApiService = new Mock();
        var decorator = new LoggingDecorator(mockApiService.Object, mockLogger.Object);

// Act await decorator.GetDataAsync("test-endpoint");

// Assert mockLogger.Verify( x => x.Log( LogLevel.Information, It.IsAny(), It.Is((v, t) => v.ToString().Contains("Calling endpoint")), It.IsAny(), It.IsAny>() ), Times.Once ); } }

Bonnes pratiques

  • Suivez le principe de responsabilité unique (SRP) pour chaque décorateur
  • Utilisez l'injection de dépendances pour gérer les décorateurs
  • Évitez les chaînes de décorateurs trop longues
  • Documentez clairement l'ordre d'application des décorateurs
  • Préférez la composition à l'héritage

Considérations de performance

Les décorateurs peuvent avoir un impact sur les performances, particulièrement dans les cas suivants :

  • Chaînes de décorateurs longues
  • Opérations synchrones bloquantes
  • Utilisation excessive de la mémoire dans les décorateurs de cache

Conclusion

Le Decorator Pattern est un outil puissant dans l'écosystème .NET moderne. Il permet d'étendre les fonctionnalités de manière flexible tout en respectant les principes SOLID. Son utilisation est particulièrement pertinente dans les scénarios de logging, caching, et validation, couramment rencontrés dans le développement d'applications d'entreprise.

Pour aller plus loin, explorez l'utilisation du pattern avec les nouveautés de .NET 8, comme les Minimal APIs et le support natif pour les intercepteurs source generators.

Partager cet article
42
12

Commentaires (0)

Rejoignez la discussion

Connectez-vous pour partager votre avis et échanger avec la communauté

Première discussion

Soyez le premier à partager votre avis sur cet article !

À propos de l'auteur
Olivier Dupuy

Développeur passionné et créateur de contenu technique. Expert en développement web moderne avec ASP.NET Core, JavaScript, et technologies cloud.

Profil
Articles similaires
API versioning strategies
02 août 2025 0
C# & .NET
Cryptographie post-quantique
02 août 2025 0
C# & .NET
Géolocalisation et cartes interactives
02 août 2025 0
C# & .NET
Navigation rapide
Commentaires (0)