PHP7 Visitor

This commit is contained in:
Dominik Liebler
2016-09-22 11:29:48 +02:00
parent 19fff0aed9
commit 1c30978a70
9 changed files with 72 additions and 149 deletions

View File

@ -1,19 +0,0 @@
# Behavioral
In software engineering, behavioral design patterns are design patterns that
identify common communication patterns between objects and realize these
patterns. By doing so, these patterns increase flexibility in carrying out this
communication.
* [ChainOfResponsibilities](ChainOfResponsibilities) [:notebook:](http://en.wikipedia.org/wiki/Chain_of_responsibility_pattern)
* [Command](Command) [:notebook:](http://en.wikipedia.org/wiki/Command_pattern)
* [Iterator](Iterator) [:notebook:](http://en.wikipedia.org/wiki/Iterator_pattern)
* [Mediator](Mediator) [:notebook:](http://en.wikipedia.org/wiki/Mediator_pattern)
* [Memento](Memento) [:notebook:](http://en.wikipedia.org/wiki/Memento_pattern)
* [NullObject](NullObject) [:notebook:](http://en.wikipedia.org/wiki/Null_Object_pattern)
* [Observer](Observer) [:notebook:](http://en.wikipedia.org/wiki/Observer_pattern)
* [Specification](Specification) [:notebook:](http://en.wikipedia.org/wiki/Specification_pattern)
* [State](State) [:notebook:](http://en.wikipedia.org/wiki/State_pattern)
* [Strategy](Strategy) [:notebook:](http://en.wikipedia.org/wiki/Strategy_pattern)
* [TemplateMethod](TemplateMethod) [:notebook:](http://en.wikipedia.org/wiki/Template_method_pattern)
* [Visitor](Visitor) [:notebook:](http://en.wikipedia.org/wiki/Visitor_pattern)

View File

@ -2,29 +2,25 @@
namespace DesignPatterns\Behavioral\Visitor;
/**
* An example of a Visitor: Group.
*/
class Group extends Role
class Group implements Role
{
/**
* @var string
*/
protected $name;
private $name;
/**
* @param string $name
*/
public function __construct($name)
public function __construct(string $name)
{
$this->name = (string) $name;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
public function getName(): string
{
return 'Group: '.$this->name;
return sprintf('Group: %s', $this->name);
}
public function accept(RoleVisitorInterface $visitor)
{
$visitor->visitGroup($this);
}
}

View File

@ -31,9 +31,9 @@ RoleVisitorInterface.php
:language: php
:linenos:
RolePrintVisitor.php
RoleVisitor.php
.. literalinclude:: RolePrintVisitor.php
.. literalinclude:: RoleVisitor.php
:language: php
:linenos:

View File

@ -2,32 +2,7 @@
namespace DesignPatterns\Behavioral\Visitor;
/**
* class Role.
*/
abstract class Role
interface Role
{
/**
* This method handles a double dispatch based on the short name of the Visitor.
*
* Feel free to override it if your object must call another visiting behavior
*
* @param \DesignPatterns\Behavioral\Visitor\RoleVisitorInterface $visitor
*
* @throws \InvalidArgumentException
*/
public function accept(RoleVisitorInterface $visitor)
{
// this trick to simulate double-dispatch based on type-hinting
$klass = get_called_class();
preg_match('#([^\\\\]+)$#', $klass, $extract);
$visitingMethod = 'visit'.$extract[1];
// this ensures strong typing with visitor interface, not some visitor objects
if (!method_exists(__NAMESPACE__.'\RoleVisitorInterface', $visitingMethod)) {
throw new \InvalidArgumentException("The visitor you provide cannot visit a $klass instance");
}
call_user_func(array($visitor, $visitingMethod), $this);
}
public function accept(RoleVisitorInterface $visitor);
}

View File

@ -1,27 +0,0 @@
<?php
namespace DesignPatterns\Behavioral\Visitor;
/**
* Visitor Pattern.
*
* An implementation of a concrete Visitor
*/
class RolePrintVisitor implements RoleVisitorInterface
{
/**
* {@inheritdoc}
*/
public function visitGroup(Group $role)
{
echo 'Role: '.$role->getName();
}
/**
* {@inheritdoc}
*/
public function visitUser(User $role)
{
echo 'Role: '.$role->getName();
}
}

View File

@ -0,0 +1,29 @@
<?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;
}
}

View File

@ -3,29 +3,12 @@
namespace DesignPatterns\Behavioral\Visitor;
/**
* Visitor Pattern.
*
* The contract for the visitor.
*
* Note 1 : in C++ or Java, with method polymorphism based on type-hint, there are many
* methods visit() with different type for the 'role' parameter.
*
* Note 2 : the visitor must not choose itself which method to
* invoke, it is the Visitee that make this decision.
* Note: the visitor must not choose itself which method to
* invoke, it is the Visitee that make this decision
*/
interface RoleVisitorInterface
{
/**
* Visit a User object.
*
* @param \DesignPatterns\Behavioral\Visitor\User $role
*/
public function visitUser(User $role);
/**
* Visit a Group object.
*
* @param \DesignPatterns\Behavioral\Visitor\Group $role
*/
public function visitGroup(Group $role);
}

View File

@ -4,42 +4,34 @@ namespace DesignPatterns\Tests\Visitor\Tests;
use DesignPatterns\Behavioral\Visitor;
/**
* VisitorTest tests the visitor pattern.
*/
class VisitorTest extends \PHPUnit_Framework_TestCase
{
protected $visitor;
/**
* @var Visitor\RoleVisitor
*/
private $visitor;
protected function setUp()
{
$this->visitor = new Visitor\RolePrintVisitor();
$this->visitor = new Visitor\RoleVisitor();
}
public function getRole()
public function provideRoles()
{
return array(
array(new Visitor\User('Dominik'), 'Role: User Dominik'),
array(new Visitor\Group('Administrators'), 'Role: Group: Administrators'),
);
return [
[new Visitor\User('Dominik')],
[new Visitor\Group('Administrators')],
];
}
/**
* @dataProvider getRole
* @dataProvider provideRoles
*
* @param Visitor\Role $role
*/
public function testVisitSomeRole(Visitor\Role $role, $expect)
public function testVisitSomeRole(Visitor\Role $role)
{
$this->expectOutputString($expect);
$role->accept($this->visitor);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Mock
*/
public function testUnknownObject()
{
$mock = $this->getMockForAbstractClass('DesignPatterns\Behavioral\Visitor\Role');
$mock->accept($this->visitor);
$this->assertSame($role, $this->visitor->getVisited()[0]);
}
}

View File

@ -2,31 +2,25 @@
namespace DesignPatterns\Behavioral\Visitor;
/**
* Visitor Pattern.
*
* One example for a visitee. Each visitee has to extends Role
*/
class User extends Role
class User implements Role
{
/**
* @var string
*/
protected $name;
private $name;
/**
* @param string $name
*/
public function __construct($name)
public function __construct(string $name)
{
$this->name = (string) $name;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
public function getName(): string
{
return 'User '.$this->name;
return sprintf('User %s', $this->name);
}
public function accept(RoleVisitorInterface $visitor)
{
$visitor->visitUser($this);
}
}