Аналогія
Туристи зібралися до Дубаї. Спочатку їм потрібен спосіб потрапити туди (віза). Після прибуття вони відвідуватимуть будь-яку частину міста, не питаючи дозволу, ходити де заманеться. Просто скажіть їм про якесь місце — і туристи можуть там побувати. Шаблон відвідувачів допомагає додавати місця для відвідування.
Стисло
Шаблон «Відвідувач» дозволяє додавати майбутні операції для об’єктів без їхнього модифікування.
Вікіпедія
Шаблон “Відвідувач” – це спосіб відокремлення алгоритму від структури об’єкта, в якій він оперує. Результат відділення – можливість додавати нові операції до існуючих структур об’єктів без їх модифікування. Це один із способів дотримання принципу відкритості/закритості (open/closed principle).
Приклад
Візьмемо зоопарк: у нас є кілька видів тварин, і нам потрібно послухати звуки, які вони видають.
// Місце відвідування
interface Animal
{
public function accept(AnimalOperation $operation);
}
// Відвідувач
interface AnimalOperation
{
public function visitMonkey(Monkey $ monkey);
public function visitLion(Lion $lion);
public function visitDolphin(Dolphin $dolphin);
}
Реалізуємо тварин:
class Monkey implements Animal
{
public function shout()
{
echo 'Ooh oo aa aa!';
}
public function accept(AnimalOperation $operation)
{
$operation->visitMonkey($this);
}
}
class Lion implements Animal
{
public function roar()
{
echo 'Roaaar!';
}
public function accept(AnimalOperation $operation)
{
$operation->visitLion($this);
}
}
class Dolphin implements Animal
{
public function speak()
{
echo 'Tuut tuttu tuutt!';
}
public function accept(AnimalOperation $operation)
{
$operation->visitDolphin($this);
}
}
Реалізуємо відвідувача:
class Speak implements AnimalOperation
{
public function visitMonkey(Monkey $monkey)
{
$monkey->shout();
}
public function visitLion(Lion $lion)
{
$lion->roar();
}
public function visitDolphin(Dolphin $dolphin)
{
$dolphin->speak();
}
}
Використання:
$ Monkey = New Monkey ();
$ lion = New Lion ();
$ dolphin = New Dolphin ();
$ Speak = New Speak ();
$monkey->accept($speak); // Уа-уа-уааааа!
$lion->accept($speak); // Ррррррррр!
$dolphin->accept($speak); // Туут тутт туут!
Це можна було зробити просто за допомогою ієрархії успадкування, але тоді довелося модифікувати тварин при кожному додаванні до них нових дій. А тут міняти їх не треба. Наприклад, ми можемо додати тваринам стрибки, створивши нового відвідувача:
class Jump implements AnimalOperation
{
public function visitMonkey(Monkey $monkey)
{
echo 'Jumped 20 feet high! on to the tree!';
}
public function visitLion(Lion $lion)
{
echo 'Jumped 7 feet! Back on the ground!';
}
public function visitDolphin(Dolphin $dolphin)
{
echo 'Walked on water a little and disappeared';
}
}
Використання:
$jump = новий Jump();
$monkey->accept($speak); // Ooh oo aa aa!
$monkey->accept($jump); // Jumped 20 feet high! on to the tree!
$lion->accept($speak); // Roaaar!
$lion->accept($jump); // Jumped 7 feet! Back on the ground!
$dolphin->accept($speak); // Tuut tutt tuutt!
$dolphin->accept($jump); // Walked on water a little and disappeared