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 Viisitor.
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 public interface Visitor {
2 public void visit(Klient klient);
3
4 public void visit(Zamówienie zamówienie);
5
6 public void visit(Przedmiot przedmiot);
7 }
8
1 public interface Visitable {
2 public void accept(Visitor visitor);
3 }
4
1 public class GrupaKlientów implements Visitable {
2 private List<Klient> klienci = new ArrayList<Klient>();
3
4 public void accept(Visitor visitor) {
5 for (Iterator it = klienci.iterator(); it.hasNext();) {
6 ((Klient) it.next()).accept(visitor);
7 }
8 }
9
10 public void addKlient(Klient klient) {
11 klienci.add(klient);
12 }
13 }
14
1 public class Klient implements Visitable {
2 private String name;
3 private List<Zamówienie> zamówienia = new ArrayList<Zamówienie>();
4
5
6 public Klient(String name) {
7 this.name = name;
8 }
9
10 public void dodajZamówienie(Zamówienie zamówienie) {
11 zamówienia.add(zamówienie);
12 }
13
14 public void accept(Visitor visitor) {
15 visitor.visit(this);
16 for (Iterator it = zamówienia.iterator(); it.hasNext();) {
17 ((Zamówienie) it.next()).accept(visitor);
18 }
19
20 }
21
22 public void setName(String name) {
23 this.name = name;
24 }
25
26 public String getName() {
27 return name;
28
29 }
30 }
31
1 public class Zamówienie implements Visitable {
2 private String name;
3 private List<Przedmiot> przedmioty = new ArrayList<Przedmiot>();
4
5 public Zamówienie(String name) {
6 this.name = name;
7 }
8
9 public Zamówienie(String name, String przedmiot) {
10 this.name = name;
11 this.dodajPrzedmiot(new Przedmiot(przedmiot));
12 }
13
14 public void setName(String name) {
15 this.name = name;
16 }
17
18 public void accept(Visitor visitor) {
19
20 visitor.visit(this);
21 for (Iterator it = przedmioty.iterator(); it.hasNext();) {
22 ((Przedmiot) it.next()).accept(visitor);
23 }
24 }
25
26 public String getName() {
27 return name;
28 }
29
30 public void dodajPrzedmiot(Przedmiot przedmiot) {
31 przedmioty.add(przedmiot);
32 }
33 }
34
1 public class Przedmiot implements Visitable {
2 private String name;
3
4 public Przedmiot(String name) {
5 this.name = name;
6 }
7
8
9 public void accept(Visitor visitor) {
10 visitor.visit(this);
11 }
12
13 public String getName() {
14 return name;
15 }
16
17 public void setName(String name) {
18 this.name = name;
19 }
20 }
21
1 public class ZbieraczDanych implements Visitor {
2 private int liczbaKlientów;
3 private int liczbaZamówień;
4 private int liczbaPrzedmiotów;
5
6
7 public void visit(Klient klient) {
8 System.out.println(klient.getName());
9 liczbaKlientów++;
10 }
11
12 public void visit(Zamówienie zamówienie) {
13 System.out.println(zamówienie.getName());
14 liczbaZamówień++;
15 }
16
17 public void visit(Przedmiot przedmiot) {
18 System.out.println(przedmiot.getName());
19 liczbaPrzedmiotów++;
20
21 }
22
23 public void pokażWyniki() {
24 System.out.println("Liczba klientów:" + liczbaKlientów);
25 System.out.println("Liczba zamówień:" + liczbaZamówień);
26 System.out.println("Liczba przedmiotów: " + liczbaPrzedmiotów);
27 }
28 }
29
1 public class VisitorTester {
2 public static void main(String[] args) {
3 Klient klient1 = new Klient("Klient 1");
4
5 klient1.dodajZamówienie(new Zamówienie("zamówienie_1", "przedmiot_1"));
6 klient1.dodajZamówienie(new Zamówienie("zamówienie_2", "przedmiot_1"));
7 klient1.dodajZamówienie(new Zamówienie("zamówienie_3", "przedmiot_1"));
8
9 Klient klient2 = new Klient("Klient 2");
10 Zamówienie zamówienie = new Zamówienie("zamówienie_a");
11 zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a1"));
12 zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a2"));
13 zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a3"));
14 klient2.dodajZamówienie(zamówienie);
15 klient2.dodajZamówienie(new Zamówienie("zamówienie_b", "przedmiot_b1"));
16
17 GrupaKlientów grupaKlientów = new GrupaKlientów();
18 grupaKlientów.addKlient(klient1);
19 grupaKlientów.addKlient(klient2);
20 ZbieraczDanych zbieraczDanych = new ZbieraczDanych();
21
22 grupaKlientów.accept(zbieraczDanych);
23
24 zbieraczDanych.pokażWyniki();
25
26 }
27 }
28
Diagram wzorca:
