Odwiedzający w języku Go
Odwiedzający to behawioralny wzorzec projektowy pozwalający dodawać nowe zachowanie istniejącej hierarchii klas bez zmiany kodu jej klas.
O tym, dlaczego Odwiedzającego nie można po prostu zastąpić przeciążaniem metod, przeczytasz w naszym artykule Odwiedzający i podwójna dyspozycja.
Przykład koncepcyjny
Wzorzec Odwiedzający pozwala dodawać nowe zachowania do struktury bez konieczności modyfikowania jej. Powiedzmy, że naszym zadaniem jest utrzymanie biblioteki zawierającej różne struktury figur geometrycznych:
- Kwadrat
- Okrąg
- Trójkąt
Każda z powyższych struktur figur implementuje wspólny interfejs figury geometrycznej.
Jak tylko twoi współpracownicy zaczęli korzystać z biblioteki, napłynęła masa próśb o dodatkową funkcjonalność. Skupmy się na najprostszych: powiedzmy, że zespół poprosił o dodanie funkcjonalności getArea
(pobierz pole) do struktur.
Jest wiele sposobów na rozwiązanie tego problemu.
Pierwszą możliwością, jaka przychodzi do głowy, jest dodanie metody getArea
bezpośrednio do interfejsu figury geometrycznej, a następnie zaimplementowanie jej dla każdej z figur. Jest to najpopularniejsze podejście w przypadku tego typu sytuacji, ale wiąże się z pewnym kosztem. Jako osoba odpowiedzialna za utrzymanie biblioteki nie chcesz ryzykować zepsucia kodu za każdym razem gdy ktoś poprosi o jakieś nowe funkcje. Z drugiej strony, chcesz pozwolić innym zespołom rozszerzać twoją bibliotekę.
Drugą opcją jest umożliwienie zespołom samodzielnej implementacji potrzebnego zachowania. Nie jest to jednak zawsze możliwe, gdyż potrzebne zachowanie może być zależne od kodu prywatnego.
Trzecie rozwiązanie sugeruje zastosowanie wzorca Odwiedzający w połączeniu z powyższym rozwiązaniem. Zaczniemy od zdefiniowania interfejsu odwiedzającego:
Funkcje visitForSquare(square)
, visitForCircle(circle)
, visitForTriangle(triangle)
pozwolą dodać funkcjonalność do — odpowiednio — kwadratów, okręgów i trójkątów.
Pewnie zastanawiasz się, dlaczego nie możemy mieć tylko jednej metody visit(shape)
dla wszystkich figur. Powodem jest brak możliwości przeciążania metod w języku Go, więc nie możemy mieć metod o takiej samej nazwie, różniących się tylko parametrem.
Kolejnym istotnym krokiem jest dodanie metody accept
(przyjmij) do interfejsu figury.
Wszystkie struktury figur geometrycznych muszą definiować tę metodę w następujący sposób:
Ale zaraz, przecież nie chcieliśmy zmieniać istniejących struktur figur! Niestety — zastosowanie wzorca Odwiedzający nakłada na nas taki obowiązek. Na szczęście modyfikację trzeba wykonać tylko jednorazowo.
W przypadku dodawania innych funkcjonalności, jak getNumSides
lub getMiddleCoordinates
będziemy mogli stosować tę samą funkcję przyjmowania accept(v visitor)
bez konieczności ponownego zmieniania struktur reprezentujących figury.
Podsumowując, struktury odpowiadające poszczególnym figurom trzeba zmodyfikować tylko raz, a wszystkie nowe funkcjonalności będzie można obsłużyć tą samą funkcją przyjmującą. Jeśli zespół poprosi o funkcjonalność getArea
, możemy po prostu zdefiniować konkretną implementację interfejsu odwiedzającego i zaprogramować logikę obliczania w tej konkretnej implementacji.