訪問者模式允許將對(duì)象上的操作外包給其他對(duì)象。這樣做的主要原因是保持關(guān)注數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作的分離。但是類必須定義一個(gè)允許訪問的契約(示例中的Role::accept方法)。
契約是一個(gè)抽象類,但你也可以就是一個(gè)接口。在這種情況下,每個(gè)Visitor必須自己選擇要調(diào)用哪個(gè)方法。
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]); } }
更多建議: