Visitor

Nigdy więcej Ifów — Visitor

Jeśli, w swoim kodzie korzystasz z słowa kluczowego instanceof to prawdopodobnie ten wzorzec jest dla ciebie.

Zalety tego wzorca:

  • Zlikwidowanie if-ologi
  • Nie trzeba używać instancof oraz rzutować
  • W niektórych używania obiektów proxy, jedyne wyjście na rozpoznanie, typu obiektu.

Załóżmy, że robimy program płacowy.
W firmie jest Pracownik i Szef. (Szef ma jedno pole więcej premia)

Teraz chcemy dać wszystkim szefom premie, powiedzmy 200 zł, ale mamy tablicę pracowników, lub listę pracowników.

List pracownicy = new ArrayList();

Wielu z nas zrobi tak:

for (Pracownik pr : pracownicy) {
if (pr instanceof Szef) {
Szef szefu = (Szef) pr;
szefu.premia += 200f;
System.out.println("szef dostał premie");
}
}

W sumie działa, ale co jeśli pojawi się np. Kierownik, który też ma pole premia, a dodajemy ta premię w kilku miejscach w programie. Możemy się pogubić i zapomnieć o dodaniu w jednym miejscu if-a. Już nie mówiąc o pracy nad czyimś kodem. Wtedy program nie będzie działał poprawnie. No i kolejną sprawą jest to nie ładne rzutowanie typów. Rada: zastosować wzorzec visitor.

Główną częścią tego wzorca jest interfejs lub klasa abstrakcyjna Visitor. W moim przykładzie użyłem interfejsu.
Interfejs Visitor wygląda tak:

package eu.saramak.visitor;
public interface Visitor {
public void visit(Szef szef);
public void visit(Pracownik pracownik);
public void visit(Sprzataczka sprzataczk);
}

I od razu podam przykład implementacji:

package eu.saramak.visitor;
public class DodajPremieVisitor implements Visitor {
public void visit(Szef s) {
System.out.println("Dostanę premię " + s.imie);
s.premia += 200f;
}
		public void visit(Pracownik s) {
System.out.println("Nie dostanę premi "+s.imie);
}
		@Override
public void visit(Sprzataczka s) {
System.out.println("Nie dostanę premi " + s.imie);
}
}

żeby skończyć trzeba jeszcze dodać do naszych klas, Pracownik, Szef i Sprzątaczki metodę:

public void acceptVisitor(Visitor v) {
v.visit(this);
}

Przykład użycia:

public class Bootstart {
public static void main(String[] args) {
		List pracownicy = new ArrayList();
Pracownik pracownik = new Pracownik();
pracownik.imie = "Radosław";
pracownik.nazwisko = "Janas";
pracownik.placa = 20000f;
pracownicy.add(pracownik);
		Szef szef = new Szef();
szef.imie = "Mariusz";
szef.nazwisko = "Saramak";
szef.placa = 20000f;
pracownicy.add(szef);
Sprzataczka sprzataczka = new Sprzataczka();
sprzataczka.imie = "Anna";
pracownicy.add(sprzataczka);

Visitor dodajPremie = new DodajPremieVisitor();
for (Pracownik pr : pracownicy) {
pr.acceptVisitor(dodajPremie);
}
}
}

Przeanalizujmy to wszystko po kolei, mamy interfejs Visitor, który posiada tyle metod, ile jest typów pracowników dziedziczących z klasy Pracownik.
Tutaj ma zadziałać polimorfizm:
Przykładem takiej funkcji jest:

String.valueOf()

Która może przyjmować różne typy, podobnie jak nasza metoda visit().
Nasza klasa DodajPremieVisitor jest implementacją naszego interfejsu Visitor, tak naprawdę nie musimy jej od razu implementować, jako osobna klasa, możemy to zrobić jako klasa anonimowa:

for (Pracownik pr : pracownicy) {
pr.acceptVisitor(new Visitor(){
			@Override
public void visit(Szef szef) {
System.out.println("Dam premie");

}
			@Override
public void visit(Pracownik pracownik) {
System.out.println("Nie dam ");

}
			@Override
public void visit(Sprzataczka sprzataczk) {
System.out.println("Nie dam ");

}});
}

To w tej klasie, określamy jak ma się zachować nasz program, po napotkaniu, szefa, lub pracownika.
No i mamy dostęp do referencji,które są już interesującego nas typu. Nie trzeba rzutować.
Teraz wystarczy zrobić szef.premia += premia; Od razu mamy dostęp do wszystkich pól i metod szefa.
Trzecia rzecz, w naszym wzorcu to metoda acceptVisitor():

public void acceptVisitor(Visitor v) {
v.visit(this);
}

Ta metoda wywołuje w implementacji naszego Visitora odpowiednią metodę visit(), której argumentem jest this. Czyli jeśli na szefie wykonujemy metodę acceptVisit(), to dajemy mu referencje do naszej implementacji DodajPremieVisitor i wywołujemy jej metodę visit(Szef s);

Czyli bez if-ów i rzutowania. Teraz jeśli dojdzie nam kierownik, to w interfejsie dodajemy nową metodę:
visit(Kierownik k);
A eclips, podpowie nam gdzie nie mamy zaimplementowanej tej metody i każe nam to zrobić.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.