W aplikacjach często wykorzystuje się kolekcje takie jak List, Set, Map czy pochodne. W kolekcjach tych przetrzymuje się obiekty różnego typu. Nie jednokrotnie także buduje się kolekcje o budowie hierarchicznej, np. za pomocą wzorca Composite. Są sytuajcje, gdy musimy przeiterować się po takiej kolekcji w jakimś celu i wykonać na każdym obiekcie jakąś metodę. Np. w sytuacji przeindeksowania, lub przywrócenia utraconych danych. Problem w tym, że kolekcja jak wspomniano może zawierać kilka różnych obiektów i każdy powinien być traktowany inaczej. Korzystanie w takiej sytuacji z instanceof nie jest do końca zgodne z myślą projektowania obiektowego. Zatem co? Zatem wzorzec Visitor.
Visitor jest wzorcem operacyjnym, który potrafi wykonać daną operację na całej strukturze obiektów. Visitor pozwala na zdefiniowanie nowych operacji na elementach struktury, bez zmiany klas tych elementów.
We wzorcu mamy aktorów:
- Visitor: interfejs który definiuje metody dla wszystkich odwiedzanych obiektów. metody powinny być przeciążane aby miały taką samę nazwę. Wybór metody zależny jest od obiektu który jest podany jako parametr.
- ConcreteVisitor – implementacja interfejsu Visitor. Każdy ConcreteVisitor jest budowany dla różnego rodzaju operacji, możemy w systemie zbudować kilka Visitorów i każdy będzie do innego celu.
- Visitable – abstakcja – interfejs który jest implementowany przez obiekty, które mają być wizytowane. każdy obiekt kolekcji powinien implementować ten interfejs.
- ConcreteVisitable – Klasy implementujące interfejs Visitable definiujące metodę accept(). Visitor poprzez tą metode dostaje się do wnętrza obiektu wizytowanego.
- ObjectStruture – Klasa zawierająca wszystki obiekty, które moga być odwiedzone. Klasa udostępnia iterator, który pozwala na przemieszczanie sie między kolejnymi składnikami kolekcji. ObjectStructure może być klasą lub całą strukturą klas jak np. Composite.
Sprawdźmy:
Posiadamy sklep internetowy: Część struktury składa się z następujących elementów:
GrupaKlientów, Klient, Zamówienie i Przedmiot. Każde zamówienie posiada co najmniej jedne przedmiot. Każdy klient posiada conajmniej jedno zamówienie.
Chcemy wygenerować raporty dotyczące grup użytkowników naszego sklepu.
Tworzym dwa interfejsy : Visitor i Visitable.
Musimy utworzyć klasę zbierającą dane (ZbieraczDanych), która będzie Visitorem.
Klasy GrupaKlientów, Klient, Zamówienie i Przedmiot muszą implementować interfejs Visitable.
Klasa kliencka która zarządza całością.
Sprawdźmy jak to wygląda:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
public interface Visitor { public void visit(Klient klient); public void visit(Zamowienie zamowienie); public void visit(Przedmiot przedmiot); } public interface Visitable { public void accept(Visitor visitor); } public class GrupaKlientow implements Visitable { private List<Klient> klienci = new ArrayList<Klient>(); public void accept(Visitor visitor) { for (Iterator it = klienci.iterator(); it.hasNext();) { ((Klient) it.next()).accept(visitor); } } public void addKlient(Klient klient) { klienci.add(klient); } } public class Klient implements Visitable { private String name; private List<Zamowienie> zamowienia = new ArrayList<Zamowienie>(); public Klient(String name) { this.name = name; } public void dodajZamowienie(Zamowienie zamowienie) { zamowienia.add(zamowienie); } public void accept(Visitor visitor) { visitor.visit(this); for (Iterator it = zamowienia.iterator(); it.hasNext();) { ((Zamowienie) it.next()).accept(visitor); } } public void setName(String name) { this.name = name; } public String getName() { return name; } } public class Zamowienie implements Visitable { private String name; private List<Przedmiot> przedmioty = new ArrayList<Przedmiot>(); public Zamowienie(String name) { this.name = name; } public Zamowienie(String name, String przedmiot) { this.name = name; this.dodajPrzedmiot(new Przedmiot(przedmiot)); } public void setName(String name) { this.name = name; } public void accept(Visitor visitor) { visitor.visit(this); for (Iterator it = przedmioty.iterator(); it.hasNext();) { ((Przedmiot) it.next()).accept(visitor); } } public String getName() { return name; } public void dodajPrzedmiot(Przedmiot przedmiot) { przedmioty.add(przedmiot); } } public class Przedmiot implements Visitable { private String name; public Przedmiot(String name) { this.name = name; } public void accept(Visitor visitor) { visitor.visit(this); } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class ZbieraczDanych implements Visitor { private int liczbaKlientow; private int liczbaZamowien; private int liczbaPrzedmiotow; public void visit(Klient klient) { System.out.println(klient.getName()); liczbaKlientow++; } public void visit(Zamowienie zamowienie) { System.out.println(zamowienie.getName()); liczbaZamowien++; } public void visit(Przedmiot przedmiot) { System.out.println(przedmiot.getName()); liczbaPrzedmiotow++; } public void pokazWyniki() { System.out.println("Liczba klientów:" + liczbaKlientow); System.out.println("Liczba zamówień:" + liczbaZamowien); System.out.println("Liczba przedmiotów: " + liczbaPrzedmiotow); } } public class VisitorTester { public static void main(String[] args) { Klient klient1 = new Klient("Klient 1"); klient1.dodajZamowienie(new Zamowienie("zamówienie_1", "przedmiot_1")); klient1.dodajZamowienie(new Zamowienie("zamówienie_2", "przedmiot_1")); klient1.dodajZamowienie(new Zamowienie("zamówienie_3", "przedmiot_1")); Klient klient2 = new Klient("Klient 2"); Zamowienie zamowienie = new Zamowienie("zamówienie_a"); zamowienie.dodajPrzedmiot(new Przedmiot("przedmiot_a1")); zamowienie.dodajPrzedmiot(new Przedmiot("przedmiot_a2")); zamowienie.dodajPrzedmiot(new Przedmiot("przedmiot_a3")); klient2.dodajZamowienie(zamowienie); klient2.dodajZamowienie(new Zamowienie("zamówienie_b", "przedmiot_b1")); GrupaKlientow grupaKlientow = new GrupaKlientow(); grupaKlientow.addKlient(klient1); grupaKlientow.addKlient(klient2); ZbieraczDanych zbieraczDanych = new ZbieraczDanych(); grupaKlientow.accept(zbieraczDanych); zbieraczDanych.pokazWyniki(); } } |
Diagram wzorca: