mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-08-04 22:17:25 +02:00
full visitor pattern withh double dispatch
This commit is contained in:
51
Tests/Visitor/VisitorTest.php
Normal file
51
Tests/Visitor/VisitorTest.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DesignPatternPHP
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace DesignPatterns\Tests\Visitor;
|
||||||
|
|
||||||
|
use DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VisitorTest tests the visitor pattern
|
||||||
|
*/
|
||||||
|
class VisitorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $visitor;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->visitor = new Visitor\RolePrintVisitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRole()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(new Visitor\User("Dominik"), 'Role: User Dominik'),
|
||||||
|
array(new Visitor\Group("Administrators"), 'Role: Group: Administrators')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getRole
|
||||||
|
*/
|
||||||
|
public function testVisitSomeRole(Visitor\Role $role, $expect)
|
||||||
|
{
|
||||||
|
$this->expectOutputString($expect);
|
||||||
|
$role->accept($this->visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException InvalidArgumentException
|
||||||
|
* @expectedExceptionMessage Mock
|
||||||
|
*/
|
||||||
|
public function testUnknownObject()
|
||||||
|
{
|
||||||
|
$mock = $this->getMockForAbstractClass('DesignPatterns\Visitor\Role');
|
||||||
|
$mock->accept($this->visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
Visitor/Group.php
Normal file
32
Visitor/Group.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example of a Visitee : Group
|
||||||
|
*/
|
||||||
|
class Group extends Role
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function __construct($name)
|
||||||
|
{
|
||||||
|
$this->name = (string) $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return "Group: " . $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
Visitor/Role.php
Normal file
38
Visitor/Role.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor Pattern
|
||||||
|
*
|
||||||
|
* Purpose:
|
||||||
|
* The Visitor Pattern lets you outsource operations on objects to other objects. The main reason to do this is to keep
|
||||||
|
* a seperation of concerns. But classes have to define an contract to allow visitors (the "accept" method in the example below).
|
||||||
|
*
|
||||||
|
* The contract is an abstract class but you can have also a clean interface
|
||||||
|
*/
|
||||||
|
abstract class Role
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method handle a double dispatch based on the shortname of the Visitee
|
||||||
|
*
|
||||||
|
* Feel free to override it if your object must call another visiting behavior
|
||||||
|
*
|
||||||
|
* @param \DesignPatterns\Visitor\RoleVisitor $visitor
|
||||||
|
*/
|
||||||
|
public function accept(RoleVisitor $visitor)
|
||||||
|
{
|
||||||
|
// this trick to simulate double-dispatch based on type-hinting
|
||||||
|
$fqcn = get_called_class();
|
||||||
|
preg_match('#([^\\\\]+)$#', $fqcn, $extract);
|
||||||
|
$visitingMethod = 'visit' . $extract[1];
|
||||||
|
|
||||||
|
if (!method_exists($visitor, $visitingMethod)) {
|
||||||
|
throw new \InvalidArgumentException("The visitor you provide cannot visit a $fqcn object");
|
||||||
|
}
|
||||||
|
|
||||||
|
call_user_func(array($visitor, $visitingMethod), $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
Visitor/RolePrintVisitor.php
Normal file
23
Visitor/RolePrintVisitor.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor Pattern
|
||||||
|
*
|
||||||
|
* An implementation of a Visitor
|
||||||
|
*/
|
||||||
|
class RolePrintVisitor implements RoleVisitor
|
||||||
|
{
|
||||||
|
|
||||||
|
public function visitGroup(Group $role)
|
||||||
|
{
|
||||||
|
echo "Role: " . $role->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function visitUser(User $role)
|
||||||
|
{
|
||||||
|
echo "Role: " . $role->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
Visitor/RoleVisitor.php
Normal file
26
Visitor/RoleVisitor.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor Pattern
|
||||||
|
*
|
||||||
|
* The contract for the visitor
|
||||||
|
*/
|
||||||
|
interface RoleVisitor
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit a user object
|
||||||
|
*
|
||||||
|
* @param \DesignPatterns\Visitor\User $role
|
||||||
|
*/
|
||||||
|
public function visitUser(User $role);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit a Group objet
|
||||||
|
*
|
||||||
|
* @param \DesignPatterns\Visitor\Group $role
|
||||||
|
*/
|
||||||
|
public function visitGroup(Group $role);
|
||||||
|
}
|
35
Visitor/User.php
Normal file
35
Visitor/User.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Visitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visitor Pattern
|
||||||
|
*
|
||||||
|
* One example for a visitee. Each visitee has to extends Role
|
||||||
|
*/
|
||||||
|
class User extends Role
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function __construct($name)
|
||||||
|
{
|
||||||
|
$this->name = (string) $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return "User " . $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -1,97 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace DesignPatterns;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visitor Pattern
|
|
||||||
*
|
|
||||||
* Purpose:
|
|
||||||
* The Visitor Pattern lets you outsource operations on objects to other objects. The main reason to do this is to keep
|
|
||||||
* a seperation of concerns. But classes have to define an interface to allow visitors (the "accept" method in the example below).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
interface RoleVisitor {
|
|
||||||
public function visit(Role $role);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Role {
|
|
||||||
public function getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
class RolePrintVisitor implements RoleVisitor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Role $role
|
|
||||||
*/
|
|
||||||
public function visit(Role $role) {
|
|
||||||
echo "Role: " . $role->getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class User implements Role {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function __construct($name) {
|
|
||||||
$this->name = (string) $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RoleVisitor $visitor
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function accept(RoleVisitor $visitor) {
|
|
||||||
$visitor->visit($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName() {
|
|
||||||
return "User " . $this->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Group implements Role {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function __construct($name) {
|
|
||||||
$this->name = (string) $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RoleVisitor $visitor
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function accept(RoleVisitor $visitor) {
|
|
||||||
$visitor->visit($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName() {
|
|
||||||
return "Group: " . $this->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = new User("Dominik");
|
|
||||||
$group = new Group("Administrators");
|
|
||||||
|
|
||||||
$printer = new RolePrintVisitor();
|
|
||||||
$printer->visit($user);
|
|
||||||
$printer->visit($group);
|
|
||||||
|
|
Reference in New Issue
Block a user