3.12. Visitor
3.12.1. Purpose
The Visitor Pattern lets you outsource operations on objects to otherobjects. The main reason to do this is to keep a separation of concerns.But classes have to define a contract to allow visitors (theRole::accept
method in the example).
The contract is an abstract class but you can have also a cleaninterface. In that case, each Visitor has to choose itself which methodto invoke on the visitor.
3.12.2. UML Diagram
3.12.3. Code
You can also find this code on GitHub
RoleVisitorInterface.php
- <?php
- namespace DesignPatterns\Behavioral\Visitor;
- /**
- * Note: the visitor must not choose itself which method to
- * invoke, it is the Visitee that make this decision
- */
- interface RoleVisitorInterface
- {
- public function visitUser(User $role);
- public function visitGroup(Group $role);
- }
RoleVisitor.php
- <?php
- namespace DesignPatterns\Behavioral\Visitor;
- class RoleVisitor implements RoleVisitorInterface
- {
- /**
- * @var Role[]
- */
- private $visited = [];
- public function visitGroup(Group $role)
- {
- $this->visited[] = $role;
- }
- public function visitUser(User $role)
- {
- $this->visited[] = $role;
- }
- /**
- * @return Role[]
- */
- public function getVisited(): array
- {
- return $this->visited;
- }
- }
Role.php
- <?php
- namespace DesignPatterns\Behavioral\Visitor;
- interface Role
- {
- public function accept(RoleVisitorInterface $visitor);
- }
User.php
- <?php
- namespace DesignPatterns\Behavioral\Visitor;
- class User implements Role
- {
- /**
- * @var string
- */
- private $name;
- public function __construct(string $name)
- {
- $this->name = $name;
- }
- public function getName(): string
- {
- return sprintf('User %s', $this->name);
- }
- public function accept(RoleVisitorInterface $visitor)
- {
- $visitor->visitUser($this);
- }
- }
Group.php
- <?php
- namespace DesignPatterns\Behavioral\Visitor;
- class Group implements Role
- {
- /**
- * @var string
- */
- private $name;
- public function __construct(string $name)
- {
- $this->name = $name;
- }
- public function getName(): string
- {
- return sprintf('Group: %s', $this->name);
- }
- public function accept(RoleVisitorInterface $visitor)
- {
- $visitor->visitGroup($this);
- }
- }
3.12.4. Test
Tests/VisitorTest.php
- <?php
- namespace DesignPatterns\Tests\Visitor\Tests;
- use DesignPatterns\Behavioral\Visitor;
- use PHPUnit\Framework\TestCase;
- class VisitorTest extends TestCase
- {
- /**
- * @var Visitor\RoleVisitor
- */
- private $visitor;
- protected function setUp()
- {
- $this->visitor = new Visitor\RoleVisitor();
- }
- public function provideRoles()
- {
- return [
- [new Visitor\User('Dominik')],
- [new Visitor\Group('Administrators')],
- ];
- }
- /**
- * @dataProvider provideRoles
- *
- * @param Visitor\Role $role
- */
- public function testVisitSomeRole(Visitor\Role $role)
- {
- $role->accept($this->visitor);
- $this->assertSame($role, $this->visitor->getVisited()[0]);
- }
- }