Bezoekersontwerppatroon in Java

1. Overzicht

In deze tutorial introduceren we een van de gedragsmatige GoF-ontwerppatronen: de bezoeker.

Eerst zullen we het doel en het probleem dat het probeert op te lossen, uitleggen.

Vervolgens bekijken we het UML-diagram van Visitor en de implementatie van het praktische voorbeeld.

2. Ontwerppatroon voor bezoekers

Het doel van een bezoekerspatroon is om een ​​nieuwe bewerking te definiëren zonder de wijzigingen aan een bestaande objectstructuur aan te brengen.

Stel je voor dat we een composietobject dat uit componenten bestaat. De structuur van het object staat vast - we kunnen het ofwel niet veranderen, of we zijn niet van plan om nieuwe soorten elementen aan de structuur toe te voegen.

Hoe kunnen we nu nieuwe functionaliteit aan onze code toevoegen zonder bestaande klassen te wijzigen?

Het ontwerppatroon van de bezoeker zou een antwoord kunnen zijn. Simpel gezegd, we zullen moeten doen is om een ​​functie toe te voegen die de bezoekersklasse accepteert aan elk element van de structuur.

Op die manier stellen onze componenten de implementatie van de bezoeker in staat om ze te "bezoeken" en elke vereiste actie op dat element uit te voeren.

Met andere woorden, we extraheren het algoritme dat zal worden toegepast op de objectstructuur uit de klassen.

Bijgevolg, we maken goed gebruik van het Open / Gesloten principe aangezien we de code niet zullen wijzigen, maar we kunnen de functionaliteit nog steeds uitbreiden door een nieuw Bezoeker implementatie.

3. UML-diagram

Op het UML-diagram hierboven hebben we twee implementatiehiërarchieën: gespecialiseerde bezoekers en concrete elementen.

Allereerst gebruikt de opdrachtgever een Visitor-implementatie en past deze toe op de objectstructuur. Het samengestelde object itereert over zijn componenten en past de bezoeker toe op elk ervan.

Nu, vooral relevant is dat betonnen elementen (ConcreteElementA en BetonElementB) accepteren een Bezoeker, gewoon toestaan bezoek hen.

Ten slotte is deze methode hetzelfde voor alle elementen in de structuur, het voert een dubbele verzending uit met het doorgeven zelf (via de dit trefwoord) aan de bezoekmethode van de bezoeker.

4. Implementatie

Ons voorbeeld zal op maat zijn Document object dat bestaat uit JSON- en XML-betonelementen; de elementen hebben een gemeenschappelijke abstracte superklasse, de Element.

De Document klasse:

openbare klasse Document breidt Element {Lijstelementen = nieuwe ArrayList (); // ... @Override public void accept (Visitor v) {for (Element e: this.elements) {e.accept (v); }}}

De Element class heeft een abstracte methode die de Bezoeker koppel:

openbare samenvatting ongeldig accepteren (bezoeker v);

Geef het daarom bij het maken van het nieuwe element de naam JsonElement, we zullen de implementatie van deze methode moeten bieden.

Vanwege de aard van het bezoekerspatroon zal de implementatie echter hetzelfde zijn, dus in de meeste gevallen zouden we de standaardcode moeten kopiëren en plakken van een ander, reeds bestaand element:

public class JsonElement breidt Element uit {// ... public void accept (Visitor v) {v.visit (this); }}

Aangezien onze elementen het voor elke bezoeker mogelijk maken om ze te bezoeken, laten we zeggen dat we onze Document elementen, maar elk op een andere manier, afhankelijk van het klassetype.

Daarom heeft onze bezoeker een aparte methode voor het gegeven type:

public class ElementVisitor implementeert Visitor {@Override public void visit (XmlElement xe) {System.out.println ("verwerking van een XML-element met uuid:" + xe.uuid); } @Override public void visit (JsonElement je) {System.out.println ("verwerking van een JSON-element met uuid:" + je.uuid); }}

Hier implementeert onze concrete bezoeker twee methoden, overeenkomstig één voor elk type van de Element.

Dit geeft ons toegang tot het specifieke object van de structuur waarop we de nodige acties kunnen uitvoeren.

5. Testen

Laten we voor testdoeleinden eens kijken VisitorDemoklasse:

openbare klasse VisitorDemo {openbare statische leegte hoofd (String [] args) {Bezoeker v = nieuwe ElementVisitor (); Document d = nieuw document (genererenUuid ()); d.elements.add (nieuw JsonElement (GenerUuid ())); d.elements.add (nieuw JsonElement (GenerUuid ())); d.elements.add (nieuw XmlElement (GenerUuid ())); d. aanvaarden (v); } // ...}

Eerst maken we een ElementBezoeker, het bevat het algoritme dat we op onze elementen zullen toepassen.

Vervolgens zetten we onze Document met de juiste componenten en pas de bezoeker toe die door elk element van een objectstructuur wordt geaccepteerd.

De output zou als volgt zijn:

een JSON-element verwerken met uuid: fdbc75d0-5067-49df-9567-239f38f01b04 een JSON-element verwerken met uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e een XML-element verwerken met uuid: 091bfcb8-2c68-491a-9308-42ada

Het laat zien dat de bezoeker elk element van onze structuur heeft bezocht, afhankelijk van de Element type, stuurde het de verwerking naar de juiste methode en kon het de gegevens van elk onderliggend object ophalen.

6. Minpunten

Zoals elk ontwerppatroon heeft zelfs de bezoeker zijn nadelen, in het bijzonder het gebruik ervan maakt het moeilijker om de code te onderhouden als we nieuwe elementen aan de structuur van het object moeten toevoegen.

Als we bijvoorbeeld nieuwe YamlElement, dan moeten we alle bestaande bezoekers updaten met de nieuwe methode die gewenst is voor het verwerken van dit element. Als we dit verder volgen, als we tien of meer concrete bezoekers hebben, kan het lastig zijn om ze allemaal bij te werken.

Anders dan dit, wordt bij gebruik van dit patroon de bedrijfslogica met betrekking tot een bepaald object verspreid over alle bezoekersimplementaties.

7. Conclusie

Het bezoekerspatroon is geweldig om het algoritme te scheiden van de klassen waarop het werkt. Daarnaast maakt het het toevoegen van een nieuwe bewerking gemakkelijker, gewoon door een nieuwe implementatie van de Bezoeker aan te bieden.

Bovendien zijn we niet afhankelijk van componenteninterfaces, en als ze anders zijn, is dat prima, aangezien we een apart algoritme hebben voor verwerking per betonelement.

Bovendien kan de Bezoeker uiteindelijk gegevens verzamelen op basis van het element dat hij doorkruist.

Om een ​​meer gespecialiseerde versie van het Visitor-ontwerppatroon te zien, bekijk het bezoekerspatroon in Java NIO - het gebruik van het patroon in de JDK.

Zoals gewoonlijk is de volledige code beschikbaar op het Github-project.