Ontwerppatroon voor de keten van verantwoordelijkheid in Java

1. Inleiding

In dit artikel gaan we een veel gebruikte gedragsontwerppatroon: Keten van verantwoordelijkheid.

We kunnen meer ontwerppatronen vinden in ons vorige artikel.

2. Keten van verantwoordelijkheid

Wikipedia definieert Chain of Responsibility als een ontwerppatroon dat bestaat uit "een bron van opdrachtobjecten en een reeks verwerkingsobjecten".

Elk verwerkingsobject in de keten is verantwoordelijk voor een bepaald type commando, en de verwerking is voltooid, het stuurt het commando door naar de volgende processor in de keten.

Het Chain of Responsibility-patroon is handig voor:

  • Afzender en ontvanger van een commando ontkoppelen
  • Een verwerkingsstrategie kiezen tijdens de verwerkingstijd

Laten we dus een eenvoudig voorbeeld van het patroon bekijken.

3. Voorbeeld

We gaan Chain of Responsibility gebruiken om een ​​ketting te maken voor het afhandelen van authenticatieverzoeken.

De provider van invoerverificatie is dus de opdracht, en elke authenticatieprocessor zal een afzonderlijke processor voorwerp.

Laten we eerst een abstracte basisklasse maken voor onze processors:

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

Laten we vervolgens betonprocessors maken die zich uitstrekken AuthenticationProcessor:

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

Hier hebben we twee concrete processors gemaakt voor onze inkomende autorisatieverzoeken: Gebruikersnaam Wachtwoord Processor en OAuthProcessor.

Voor elk hebben we de is geautoriseerd methode.

Laten we nu een paar tests maken:

openbare klasse ChainOfResponsibilityTest {privé statische AuthenticationProcessor getChainOfAuthProcessor () {AuthenticationProcessor oAuthProcessor = nieuwe OAuthProcessor (null); retourneer nieuwe UsernamePasswordProcessor (oAuthProcessor); } @Test openbare ongeldig gegevenOAuthProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertTrue (authProcessorChain.isAuthorized (nieuwe OAuthTokenProvider ())); } @Test openbare ongeldige gegevenSamlProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertFalse (authProcessorChain.isAuthorized (nieuwe SamlTokenProvider ())); }}

Het bovenstaande voorbeeld creëert een keten van authenticatieprocessors: GebruikersnaamPasswordProcessor -> OAuthProcessor. Bij de eerste test slaagt de autorisatie en bij de andere mislukt het.

Eerste, Gebruikersnaam Wachtwoord Processor controleert of de authenticatieleverancier een instantie is van Gebruikersnaam Wachtwoord Provider.

Niet de verwachte input zijn, Gebruikersnaam Wachtwoord Processor afgevaardigden naar OAuthProcessor.

Als laatste, de OAuthProcessor verwerkt het commando. Bij de eerste test is er een match en is de test geslaagd. In de tweede zijn er geen processors meer in de keten en als gevolg daarvan mislukt de test.

4. Implementatieprincipes

We moeten enkele belangrijke principes in gedachten houden bij het implementeren van Chain of Responsibility:

  • Elke processor in de keten krijgt zijn implementatie voor het verwerken van een commando
    • In ons voorbeeld hierboven hebben alle processors hun implementatie van is geautoriseerd
  • Elke processor in de keten moet een verwijzing hebben naar de volgende processor
    • Bovenstaande, Gebruikersnaam Wachtwoord Processor afgevaardigden naar OAuthProcessor
  • Elke processor is verantwoordelijk voor het delegeren naar de volgende processor, dus pas op voor weggevallen opdrachten
    • Nogmaals in ons voorbeeld, als de opdracht een instantie is van SamlProvider dan wordt het verzoek mogelijk niet verwerkt en is het niet geautoriseerd
  • Processors mogen geen recursieve cyclus vormen
    • In ons voorbeeld hebben we geen cyclus in onze keten: GebruikersnaamPasswordProcessor -> OAuthProcessor. Maar als we expliciet instellen Gebruikersnaam Wachtwoord Processor als volgende processor van OAuthProcessor, dan eindigen we met een kringloop in onze ketting: GebruikersnaamPasswordProcessor -> OAuthProcessor -> GebruikersnaamPasswordProcessor. Het nemen van de volgende processor in de constructor kan hierbij helpen
  • Slechts één processor in de keten verwerkt een bepaald commando
    • Als in ons voorbeeld een inkomend commando een exemplaar bevat van OAuthTokenProvider, dan alleen OAuthProcessor zal het commando afhandelen

5. Gebruik in de echte wereld

In de Java-wereld profiteren we elke dag van Chain of Responsibility. Een zo'n klassiek voorbeeld zijn Servlet-filters in Java waarmee meerdere filters een HTTP-verzoek kunnen verwerken. Hoewel in dat geval elk filter roept de ketting op in plaats van het volgende filter.

Laten we het onderstaande codefragment eens bekijken voor een beter begrip van dit patroon in Servlet Filters:

public class CustomFilter implementeert Filter {public void doFilter (ServletRequest-verzoek, ServletResponse-antwoord, FilterChain-keten) gooit IOException, ServletException {// verwerk het verzoek // geef het verzoek (dwz de opdracht) door langs de filterketen. doFilter (verzoek, antwoord ); }}

Zoals te zien is in het bovenstaande codefragment, moeten we een beroep doen op FilterChain‘S doFilter methode om het verzoek door te geven aan de volgende processor in de keten.

6. Nadelen

En nu we hebben gezien hoe interessant Chain of Responsibility is, laten we enkele nadelen in gedachten houden:

  • Meestal kan het gemakkelijk kapot gaan:
    • als een processor de volgende processor niet aanroept, wordt de opdracht verwijderd
    • als een processor de verkeerde processor aanroept, kan dit tot een cyclus leiden
  • Het kan diepe stack-traces creëren, wat de prestaties kan beïnvloeden
  • Het kan leiden tot dubbele code tussen processors, waardoor het onderhoud toeneemt

7. Conclusie

In dit artikel hebben we gesproken over Chain of Responsibility en zijn sterke en zwakke punten met behulp van een ketting om inkomende authenticatieverzoeken te autoriseren.

En, zoals altijd, is de broncode te vinden op GitHub.