purpose
The visitor pattern allows you to outsource object operations to other objects.
The main reason for this is the separation of concerns (data structure and data manipulation). But the class being visited must make a contract to accept the visitor. (See the Role::accept method in the example for details)
The contract can be an abstract class or an interface. In this case, each visitor must choose which method of the visitor to call.
UML graphics
Code
- RoleVisitorInterface.php
<?php
namespace DesignPatterns\Behavioral\Visitor;
/**
* 注意:访问者不能自行选择调用哪个方法,
* 它是由 Visitee 决定的。
*/
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);
}
}
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]);
}
}
The Growth Path of PHP Internet Architect * The Ultimate Guide to "Design Patterns"
PHP Internet Architect 50K Growth Guide + Industry Problem Solving Guide (Continuous Update)
Interview with 10 companies, get 9 offers, PHP interview questions in 2020
★If you like my article and want to communicate and learn with more senior developers, get more technical consultation and guidance related to interviews with big companies, welcome to join our group, password: phpzh
The latest PHP advanced tutorial in 2020, full series!
If the content is good, I hope everyone will support and encourage you to give a like/like, and welcome to communicate together; in addition, if you have any questions, you can suggest what you want to see in the comments.