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 :
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 :
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
