Идеята на този design pattern e да може ако искаме да добавяме още функционалност към даден обект (Component), да не го дописваме него (обектът), а да имаме друг клас (Visitor), на който да подаваме този обект, и там, използвайки методите му (на предаденият Component) да създадем допълнителната логика.
Или даже по-скоро изцяло да разделим даден клас на части, например оставяйки единият да отговаря само за данните, а в други (visitors) да имаме различните логики за работа с тези данни.
In object-oriented programming and software engineering, the Visitor Design pattern is a way of separating an algorithm from an object data structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle.
Казах „подаваме“, но имах предвид, че по-скоро приемаме „посетителят“ и му подаваме сами себе си с $this с метод, обикновено наречен accept()
public function accept (Visitor $visitor): void { $visitor->visitConcreteComponent($this); }
Ето как Visitor обектът бива допуснат в нашият Component и той сам си взема дадената инстанция ($this) с помощта на свой метод visitConcreteComponent() и го използва за да създаде допълнителна функционалност в себе си (в класът Visitor).
Този design pattern е едно типично превъплъщение на Open Close SOLID principle и много напомня на Adapter или Decorator и други, на които подаваме даден обект и те го „доразвиват“.
…the visitor design pattern is a way of separating an algorithm from an object data on which it operates…
По този начин основният клас Component може спокойно да остане максимално прост, а за него да създадем N на брой Visitor класа, които да му добавят нужната допълнителна логика. Особено когато това се налага често.
Най-малкото, което печелим е, че няма да претоварим Component класа и да нарушим Single responsibility SOLID принципа, както и няма да му пишем наследници, което значи, че ще използваме composition over inheritance.
Ето я в основни линии идеята на Visitor design pattern – да можем да „разхвърляме“ логиката на даден клас по други класове (Visitors), а не да го претоварваме като пишем цялата логика само в него.
Отделно, представете си че имаме даден алгоритъм, който трябва да реализираме в повече от един клас. Във всеки ли да го реализираме?
Не е ли по-елегантно да го реализираме в един Visitor клас,
и с accept()-овете на тези отделните, повече от един Component-и,
да му ги подаваме, и той (Visitor-ът) да ги „посещава“ и да реализира тази логика, използвайки ги.
Един Component разбира се може да има много Visitor-и. Даже не „да има“, а „да използва“, защото това са два различни класови структури (Component и Visitor), тоест, няма пряка връзка между тях, като наследяване например.
- Visitor makes adding new operations easy. You can define a new operation over an object structure simply by adding a new visitor.
- A visitor gathers related operations and separates unrelated ones. Демек, във отделни Visitors можеш да си капсулираш сходни по смисъл функционалности.
- Visitor can visit objects that don’t have a common parent class, as long as it can use the Component functionality.
- Accumulating state. Visitors can accumulate state as they visit each element in the object structure. Without a visitor, this state would be passed as extra arguments to the operations that perform the traversal, or they might appear as global variables. Visitor-ите могат да акумулират информация от отделните обекти, които му се подават.
- Breaking encapsulation. Visitor’s approach assumes that the ConcreteElement interface is powerful enough to let visitors do their job. As a result, the pattern often forces you to provide public operations that access an element’s internal state, which may compromise its encapsulation.
Като недостатък, понеже Visitor-ите трябва да могат да използват public функционалността на подаваните им обекти, може да се наложи те (използваните обекти) да имат повече public методи и пропъртита, което може да намали data hiding.
Литература:
https://refactoring.guru/design-patterns/visitor/php/example