mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-08-12 09:54:09 +02:00
start a restructure
This commit is contained in:
30
Behavioral/Visitor/Group.php
Normal file
30
Behavioral/Visitor/Group.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Visitor;
|
||||
|
||||
/**
|
||||
* An example of a Visitor: 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;
|
||||
}
|
||||
}
|
8
Behavioral/Visitor/README.md
Normal file
8
Behavioral/Visitor/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Visitor
|
||||
|
||||
## Purpose
|
||||
|
||||
The Visitor Pattern lets you outsource operations on objects to other objects. The main reason to do this is to keep a separation of concerns.
|
||||
But classes have to define a contract to allow visitors (the `Role::accept` method in the example).
|
||||
|
||||
The contract is an abstract class but you can have also a clean interface. In that case, each Visitor has to choose itself which method to invoke on the visitor.
|
33
Behavioral/Visitor/Role.php
Normal file
33
Behavioral/Visitor/Role.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Visitor;
|
||||
|
||||
/**
|
||||
* class Role
|
||||
*/
|
||||
abstract class 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\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);
|
||||
}
|
||||
}
|
27
Behavioral/Visitor/RolePrintVisitor.php
Normal file
27
Behavioral/Visitor/RolePrintVisitor.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\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();
|
||||
}
|
||||
}
|
31
Behavioral/Visitor/RoleVisitorInterface.php
Normal file
31
Behavioral/Visitor/RoleVisitorInterface.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\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.
|
||||
*/
|
||||
interface RoleVisitorInterface
|
||||
{
|
||||
/**
|
||||
* Visit a User object
|
||||
*
|
||||
* @param \DesignPatterns\Visitor\User $role
|
||||
*/
|
||||
public function visitUser(User $role);
|
||||
|
||||
/**
|
||||
* Visit a Group object
|
||||
*
|
||||
* @param \DesignPatterns\Visitor\Group $role
|
||||
*/
|
||||
public function visitGroup(Group $role);
|
||||
}
|
47
Behavioral/Visitor/Test/VisitorTest.php
Normal file
47
Behavioral/Visitor/Test/VisitorTest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
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
Behavioral/Visitor/User.php
Normal file
32
Behavioral/Visitor/User.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user