PHP设计模式 PHP 访问者模式

2024-02-26 开发教程 PHP设计模式 匿名 2

目的

访问者模式允许将对象上的操作外包给其他对象。这样做的主要原因是保持关注数据结构和数据操作的分离。但是类必须定义一个允许访问的契约(示例中的Role::accept方法)。

契约是一个抽象类,但你也可以就是一个接口。在这种情况下,每个Visitor必须自己选择要调用哪个方法。

UML 图

代码

RoleVisitor.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Visitor;
/**
* Note: the visitor must not choose itself which method to
* invoke, it is the visited object that makes this decision
*/
interface RoleVisitor
{
public function visitUser(User $role);
public function visitGroup(Group $role);
}

RecordingVisitor.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Visitor;
class RecordingVisitor implements RoleVisitor
{
/**
* @var Role[]
*/
private array $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
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Visitor;
interface Role
{
public function accept(RoleVisitor $visitor);
}

User.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Visitor;
class User implements Role
{
public function __construct(private string $name)
{
}
public function getName(): string
{
return sprintf('User %s', $this->name);
}
public function accept(RoleVisitor $visitor)
{
$visitor->visitUser($this);
}
}

Group.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Visitor;
class Group implements Role
{
public function __construct(private string $name)
{
}
public function getName(): string
{
return sprintf('Group: %s', $this->name);
}
public function accept(RoleVisitor $visitor)
{
$visitor->visitGroup($this);
}
}

测试

Tests/VisitorTest.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Tests\Visitor\Tests;
use DesignPatterns\Behavioral\Visitor\RecordingVisitor;
use DesignPatterns\Behavioral\Visitor\User;
use DesignPatterns\Behavioral\Visitor\Group;
use DesignPatterns\Behavioral\Visitor\Role;
use DesignPatterns\Behavioral\Visitor;
use PHPUnit\Framework\TestCase;
class VisitorTest extends TestCase
{
private RecordingVisitor $visitor;
protected function setUp(): void
{
$this->visitor = new RecordingVisitor();
}
public function provideRoles()
{
return [
[new User('Dominik')],
[new Group('Administrators')],
];
}
/**
* @dataProvider provideRoles
*/
public function testVisitSomeRole(Role $role)
{
$role->accept($this->visitor);
$this->assertSame($role, $this->visitor->getVisited()[0]);
}
}