Aller au contenu

Proxy

Design Pattern Proxy

Un intermédiaire qui contrôle les accès

Introduction

Le design pattern Proxy est un pattern de conception structurel dont l’objectif est de contrôler l’accès à un objet. Au lieu d’utiliser directement l’objet exécutant un traitement, le code client utilise un proxy. Ce proxy implémente la même interface que l’objet réel, puis décide comment déléguer l’appel : il peut vérifier les autorisations requises, loguer l’accès, ou mettre un résultat en cache pour une utilisation ultérieure.

Ce design pattern est utile lorsque l’accès ou l’utilisation d’un objet doit être contrôlé, sans modifier son implémentation ni le code client qui l’utilise.

Problématique

Imaginons une application permettant de consulter des documents confidentiels. La classe DocumentService permet d'obtenir le contenu d’un document à partir de son identifiant unique :

public interface IDocumentService
{
    string GetContent(Guid documentId);
}

public class DocumentService : IDocumentService
{
    public string GetContent(Guid docUid)
    {
        Console.WriteLine("Chargement du document.");

        return $"Contenu du document dont l'identifiant est {docUid}.";
    }
}

Le code client peut alors utiliser directement ce service :

string content = documentService.GetContent(documentId);

Cependant, tous les utilisateurs ne doivent pas pouvoir consulter tous les documents. Sans abstraction supplémentaire, le code client doit vérifier lui-même les droits d’accès avant d’appeler le service. Cette logique risquerait alors d’être dupliquée à différents endroits et mélangée au code métier de l’application.

Présentation du design pattern Proxy

Les principaux acteurs de ce design pattern sont les suivants :

  • Le Subject : interface commune utilisée par le code client

  • Le RealSubject : objet qui réalise le traitement

  • Le Proxy : objet intermédiaire qui contrôle l’accès au RealSubject

  • Le Client : code qui utilise le Subject, sans nécessairement savoir s’il communique avec le Proxy ou le RealSubject

Conception

Voici un schéma de conception présentant le design pattern Proxy :

Design Pattern Proxy : conception

Dans notre exemple :

  • L'interface IDocumentService est le Subject

  • La classe DocumentService est le RealSubject

  • La classe SecuredDocumentServiceProxy est le Proxy

  • La classe CodeClient est le Client

Le Proxy et le RealSubject implémentent la même interface, ce qui permet au Proxy de se substituer à l’objet réel de manière transparente pour le code client. Son objectif n’est pas d’ajouter une nouvelle fonctionnalité métier, mais de maîtriser les conditions dans lesquelles l’objet réel est utilisé.

Le Client manipule un objet de type IDocumentService. Cet objet peut être une instance de la classe SecuredDocumentServiceProxy (le Client n'a pas besoin de connaître cette classe).

Implémentation

Le proxy, représenté par la classe SecuredDocumentServiceProxy, obtient une instance du RealSubject (DocumentService) au travers du Subject (IDocumentService), via une injection de dépendance, ainsi qu’un service chargé de déterminer si l’utilisateur dispose des droits nécessaires (IAuthorizationService) :

public interface IAuthorizationService
{
    bool CanReadDocument(Guid documentId);
}

public class SecuredDocumentServiceProxy : IDocumentService
{
    private readonly IDocumentService _documentService;
    private readonly IAuthorizationService _authorizationService;

    public SecuredDocumentServiceProxy(
        IDocumentService documentService,
        IAuthorizationService authorizationService)
    {
        _documentService = documentService;
        _authorizationService = authorizationService;
    }

    public string GetContent(Guid documentId)
    {
        if (!_authorizationService.CanReadDocument(documentId))
            throw new UnauthorizedAccessException("Vous n'êtes pas autorisé à consulter ce document.");

        Console.WriteLine("Accès au document autorisé.");

        return _documentService.GetContent(documentId);
    }
}

Avant d’obtenir le contenu du document, le Proxy vérifie que l’utilisateur dispose des droits nécessaires. Si l’accès est autorisé, il délègue l’appel au RealSubject, qui retourne son contenu.

IDocumentService documentService = new SecuredDocumentServiceProxy(
    _documentService,
    _authorizationService);

var content = documentService.GetContent(documentId);

Le code client ne sait pas qu’un contrôle d’autorisation est effectué. Il appelle la même méthode, via la même interface, comme s’il utilisait directement l'instance de la classe DocumentService.

Ne pas confondre l'usage des design pattern Proxy, Decorator et Facade

Les design patterns Proxy, Decorator et Facade peuvent sembler proches, car ils positionnent un ou plusieurs objets supplémentaires entre le code client et les objets utilisés. Ils répondent à des objectifs différents :

  • Design pattern Proxy : contrôle l’accès à un objet ou les conditions dans lesquelles celui-ci est utilisé (tout en exposant la même interface)

  • Design pattern Decorator : enrichit dynamiquement le comportement d’un objet en lui ajoutant des responsabilités (tout en conservant la même interface)

  • Design pattern Facade : fournit une interface simplifiée permettant d’utiliser un sous-système