Modèle de conception de chaîne de responsabilité en Java

1. Introduction

Dans cet article, nous allons examiner un modèle de conception comportementale largement utilisé : la chaîne de responsabilité .

Nous pouvons trouver plus de modèles de conception dans notre article précédent.

2. Chaîne de responsabilité

Wikipedia définit la chaîne de responsabilité comme un modèle de conception consistant en «une source d'objets de commande et une série d'objets de traitement».

Chaque objet de traitement de la chaîne est responsable d'un certain type de commande, et le traitement est effectué, il transmet la commande au processeur suivant de la chaîne.

Le modèle de chaîne de responsabilité est pratique pour:

  • Découpler un émetteur et un destinataire d'une commande
  • Choisir une stratégie de traitement au moment du traitement

Alors, voyons un exemple simple du modèle.

3. Exemple

Nous allons utiliser Chain of Responsibility pour créer une chaîne de traitement des demandes d'authentification.

Ainsi, le fournisseur d'authentification d'entrée sera la commande et chaque processeur d'authentification sera un objet processeur distinct .

Créons d'abord une classe de base abstraite pour nos processeurs:

public abstract class AuthenticationProcessor { public AuthenticationProcessor nextProcessor; // standard constructors public abstract boolean isAuthorized(AuthenticationProvider authProvider); }

Ensuite, créons des processeurs concrets qui étendent AuthenticationProcessor :

public class OAuthProcessor extends AuthenticationProcessor { public OAuthProcessor(AuthenticationProcessor nextProcessor) { super(nextProcessor); } @Override public boolean isAuthorized(AuthenticationProvider authProvider) { if (authProvider instanceof OAuthTokenProvider) { return true; } else if (nextProcessor != null) { return nextProcessor.isAuthorized(authProvider); } return false; } }
public class UsernamePasswordProcessor extends AuthenticationProcessor { public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) { super(nextProcessor); } @Override public boolean isAuthorized(AuthenticationProvider authProvider) { if (authProvider instanceof UsernamePasswordProvider) { return true; } else if (nextProcessor != null) { return nextProcessor.isAuthorized(authProvider); } return false; } }

Ici, nous avons créé deux processeurs concrets pour nos demandes d'autorisation entrantes: UsernamePasswordProcessor et OAuthProcessor .

Pour chacun d'eux, nous avons remplacé la méthode isAuthorized .

Créons maintenant quelques tests:

public class ChainOfResponsibilityTest { private static AuthenticationProcessor getChainOfAuthProcessor() { AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null); return new UsernamePasswordProcessor(oAuthProcessor); } @Test public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() { AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor(); assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider())); } @Test public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() { AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor(); assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider())); } }

L'exemple ci-dessus crée une chaîne de processeurs d'authentification: UsernamePasswordProcessor -> OAuthProcessor . Dans le premier test, l'autorisation réussit et dans l'autre, elle échoue.

Tout d'abord, UsernamePasswordProcessor vérifie si le fournisseur d'authentification est une instance de UsernamePasswordProvider .

N'étant pas l'entrée attendue, UsernamePasswordProcessor délègue à OAuthProcessor .

Enfin, OAuthProcessor traite la commande. Dans le premier test, il y a une correspondance et le test réussit. Dans le second, il n'y a plus de processeurs dans la chaîne et, par conséquent, le test échoue.

4. Principes de mise en œuvre

Nous devons garder à l'esprit quelques principes importants lors de la mise en œuvre de la chaîne de responsabilité:

  • Chaque processeur de la chaîne aura son implémentation pour traiter une commande
    • Dans notre exemple ci-dessus, tous les processeurs ont leur implémentation de isAuthorized
  • Chaque processeur de la chaîne doit avoir une référence au processeur suivant
    • Ci-dessus, UsernamePasswordProcessor délègue à OAuthProcessor
  • Chaque processeur est responsable de la délégation au processeur suivant, alors méfiez-vous des commandes abandonnées
    • Encore une fois dans notre exemple, si la commande est une instance de SamlProvider, la requête peut ne pas être traitée et ne sera pas autorisée
  • Les processeurs ne doivent pas former un cycle récursif
    • Dans notre exemple, nous n'avons pas de cycle dans notre chaîne: UsernamePasswordProcessor -> OAuthProcessor . Mais, si nous définissons explicitement UsernamePasswordProcessor comme prochain processeur d' OAuthProcessor, alors nous nous retrouvons avec un cycle dans notre chaîne : UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. Prendre le prochain processeur dans le constructeur peut vous aider
  • Un seul processeur de la chaîne gère une commande donnée
    • Dans notre exemple, si une commande entrante contient une instance de OAuthTokenProvider , alors seul OAuthProcessor gérera la commande

5. Utilisation dans le monde réel

Dans le monde Java, nous bénéficions chaque jour de Chain of Responsibility. Les filtres de servlet en Java, qui permettent à plusieurs filtres de traiter une requête HTTP, en sont un exemple classique . Bien que dans ce cas, chaque filtre invoque la chaîne au lieu du filtre suivant.

Jetons un coup d'œil à l'extrait de code ci-dessous pour une meilleure compréhension de ce modèle dans les filtres de servlet :

public class CustomFilter implements Filter { public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // process the request // pass the request (i.e. the command) along the filter chain chain.doFilter(request, response); } }

Comme on le voit dans le code extrait ci - dessus, nous avons besoin d'invoquer FilterChain de doFilter méthode afin de transmettre la demande au processeur suivant dans la chaîne.

6. Inconvénients

Et maintenant que nous avons vu à quel point la chaîne de responsabilité est intéressante, gardons à l'esprit certains inconvénients:

  • Surtout, il peut se casser facilement:
    • si un processeur ne parvient pas à appeler le processeur suivant, la commande est abandonnée
    • si un processeur appelle le mauvais processeur, cela peut conduire à un cycle
  • Il peut créer des traces de pile profondes, ce qui peut affecter les performances
  • Cela peut entraîner une duplication du code entre les processeurs, augmentant ainsi la maintenance

7. Conclusion

Dans cet article, nous avons parlé de la chaîne de responsabilité et de ses forces et faiblesses à l'aide d'une chaîne pour autoriser les demandes d'authentification entrantes.

Et, comme toujours, le code source peut être trouvé sur GitHub.