Uzorak dizajna posjetitelja na Javi

1. Pregled

U ovom uputstvu predstavit ćemo jedan od obrazaca ponašanja GoF-a - Visitor.

Prvo ćemo objasniti njegovu svrhu i problem koji pokušava riješiti.

Zatim ćemo pogledati UML dijagram posjetitelja i primjenu praktičnog primjera.

2. Uzorak dizajna posjetitelja

Svrha uzorka Visitor je definirati novu operaciju bez uvođenja modifikacija u postojeću strukturu objekta.

Zamislite da imamo kompozitniobjekt koji se sastoji od komponenata. Struktura objekta je fiksna - ili je ne možemo promijeniti ili ne planiramo dodavati nove vrste elemenata u strukturu.

Sada, kako bismo mogli dodati novu funkcionalnost našem kodu bez izmjene postojećih klasa?

Uzorak dizajna posjetitelja može biti odgovor. Jednostavno rečeno, trebamo dodati funkciju koja prihvaća klasu posjetitelja svakom elementu strukture.

Na taj će način naše komponente omogućiti posjetitelju da ih "posjeti" i izvrši sve potrebne radnje na tom elementu.

Drugim riječima, iz klasa ćemo izdvojiti algoritam koji će se primijeniti na strukturu objekta.

Slijedom toga, dobro ćemo iskoristiti princip Otvoreno / Zatvoreno jer nećemo mijenjati kôd, ali svejedno ćemo moći proširiti funkcionalnost pružanjem novog Posjetitelj provedba.

3. UML dijagram

Na gornjem UML dijagramu imamo dvije hijerarhije implementacije, specijalizirane posjetitelje i konkretne elemente.

Prije svega, klijent koristi implementaciju Visitor i primjenjuje je na strukturu objekta. Sastavljeni objekt ponavlja se preko svojih komponenata i primjenjuje posjetitelja na svaku od njih.

Sad je to posebno relevantno betonski elementi (ConcreteElementA i ConcreteElementB) prihvaćaju a Posjetitelj, jednostavno dopuštajući da posjetiti ih.

I na kraju, ova metoda je ista za sve elemente u strukturi, izvodi dvostruku otpremu s prosljeđivanjem (putem ovaj ključna riječ) na način posjeta posjetitelja.

4. Provedba

Naš će primjer biti običaj Dokument objekt koji se sastoji od JSON i XML betonskih elemenata; elementi imaju zajedničku apstraktnu superklasu, Element.

The Dokument razred:

javna klasa Document proširuje Element {Elementi popisa = novi ArrayList (); // ... @Preuzmi javno prazno prihvaćanje (Visitor v) {for (Element e: this.elements) {e.accept (v); }}}

The Element razred ima apstraktnu metodu koja prihvaća Posjetitelj sučelje:

javna sažetak praznina prihvaća (Visitor v);

Stoga, prilikom stvaranja novog elementa, dajte mu ime JsonElement, morat ćemo osigurati provedbu ove metode.

Međutim, zbog prirode obrasca Visitor, implementacija će biti ista, pa će u većini slučajeva biti potrebno da kopiramo i zalijepimo kôd uzorka iz drugog, već postojećeg elementa:

javna klasa JsonElement proširuje Element {// ... public void accept (Visitor v) {v.visit (this); }}

Budući da naši elementi omogućuju posjećivanje bilo kojeg posjetitelja, recimo da želimo obraditi svoj Dokument elementi, ali svaki od njih na drugačiji način, ovisno o vrsti klase.

Stoga će naš posjetitelj imati zasebnu metodu za danu vrstu:

javna klasa ElementVisitor implementira Visitor {@Override public void visit (XmlElement xe) {System.out.println ("obrada XML elementa s uuid:" + xe.uuid); } @Override public void visit (JsonElement je) {System.out.println ("obrada JSON elementa s uuid:" + je.uuid); }}

Ovdje naš konkretni posjetitelj primjenjuje dvije metode, što odgovara jednom za svaku vrstu Element.

To nam daje pristup određenom objektu strukture na kojem možemo izvoditi potrebne radnje.

5. Ispitivanje

Za potrebe testiranja, pogledajmo VisitorDemorazred:

javna klasa VisitorDemo {public static void main (String [] args) {Visitor v = novi ElementVisitor (); Dokument d = novi dokument (generirajUuid ()); d.elements.add (novi JsonElement (generirajUuid ())); d.elements.add (novi JsonElement (generirajUuid ())); d.elements.add (novi XmlElement (generirajUuid ())); d.prihvatiti (v); } // ...}

Prvo stvorimo ElementPosjetitelju, u njemu se nalazi algoritam koji ćemo primijeniti na naše elemente.

Dalje, postavili smo svoj Dokument s odgovarajućim komponentama i primijenite posjetitelja koji će prihvatiti svaki element objektne strukture.

Izlaz bi bio ovako:

obrada JSON elementa s uuid: fdbc75d0-5067-49df-9567-239f38f01b04 obrada JSON elementa s uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e obrada XML elementa s uuid: 091bfcb8-2c68-491a-9a

To pokazuje da je posjetitelj posjetio svaki element naše strukture, ovisno o Element tipa, poslao je obradu na odgovarajuću metodu i mogao dohvatiti podatke iz svakog temeljnog objekta.

6. Loše strane

Kao i svaki uzorak dizajna, čak i posjetitelj ima svojih loših strana, posebno njegove upotrebe otežava održavanje koda ako moramo dodati nove elemente u strukturu objekta.

Na primjer, ako dodamo novo YamlElement, tada moramo ažurirati sve postojeće posjetitelje novom metodom željenom za obradu ovog elementa. Nakon toga, ako imamo deset ili više konkretnih posjetitelja, možda bi bilo nezgodno ažurirati ih sve.

Osim ovog, kada se koristi ovaj obrazac, poslovna logika povezana s određenim objektom širi se po svim implementacijama posjetitelja.

7. Zaključak

Uzorak posjetitelja izvrstan je za odvajanje algoritma od klasa na kojima djeluje. Osim toga, to olakšava dodavanje nove operacije, samo pružajući novu implementaciju Visitora.

Nadalje, ne ovisimo o sučeljima komponenata, a ako se razlikuju, to je u redu, jer imamo zasebni algoritam za obradu po konkretnom elementu.

Štoviše, posjetitelj na kraju može prikupiti podatke na temelju elementa koji prelazi.

Da biste vidjeli specijaliziraniju verziju dizajna Visitor, pogledajte obrazac posjetitelja u Javi NIO - upotreba uzorka u JDK.

Kao i obično, cjeloviti kôd dostupan je na projektu Github.