PHP设计模式-访问者 Visitor

PHP访问者设计模式(Visitor) 可以让你对某些对象的操作外包给其他对象。它封装了一些施加于某数据结构元素之上的操作。一旦这些操作需要修改,接受这个操作的数据结构可以保持不变。

也可以这样理解:现在有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者Visitor对象;访问者实现了一个拥有一个visit方法的接口,这个方法对访问的对象结构中不同类型的元素作出不同的反应;visitor访问一次,就遍历整个对象结构,每个元素都实施accept方法,同时在每个元素的accept方法中回调访问者的visit方法,从而使访问者得以处理对象结构的每个元素。我们可以针对不同的类型的对象结构元素,设计不同的访问者实体类来完成不同的操作。

访问者设计模式类必须定义一套允许访问者的约定规则(如下面例子中的Role::accept)。该约定规则是抽象类,不过也可以是一个更简洁的接口,在那样的情况下,每个访问者必须选择用哪个方法回调访问者自己。

下面的PHP实例定义了用户User,组Group两种类型的对象结构,以及RolePrintVisitor访问者,访问者接口中定义了针对不同的对象结构采取的不同方法:

// 访问者角色接口
interface RoleVisitorInterface
{
    // 访问User对象
    public function visitUser(User $role);

    // 访问Group对象
    public function visitGroup(Group $role);
}
// 访问者,根据访问对象结构的对象不同定义不同的方法
class RolePrintVisitor implements RoleVisitorInterface
{
    public function visitUser(User $role)
    {
        echo "Role: " . $role->getName();
    }

    public function visitGroup(Group $role)
    {
        echo "Role: " . $role->getName();
    }
}
// 角色抽象类 - 对象结构
abstract class Role
{
    // 接收访问者的访问,根据Visitor的名称分发,可以重写这个方法使用访问者的其他你想用的回调方法
    public function accept(RoleVisitorInterface $visitor)
    {
        // 获得是User 还是 Group 实体类调用的,从而得出用哪个回调方法
        $klass = get_called_class(); //后期静态绑定,若是php get_class()会返回Role
        $visitingMethod = 'visit' . $klass;
        // preg_match('#([^\\\\]+)$#', $klass, $extract);
        // $visitingMethod = 'visit' . $extract[1];

        // 确保只有访问者角色接口中的定义过的方法才能回调
        if (!method_exists( 'RoleVisitorInterface', $visitingMethod)) {
            throw new Exception("The visitor you provide cannot visit a $klass instance");
        }

        call_user_func(array($visitor, $visitingMethod), $this);
    }
}
// 用户类型的对象结构
class User extends Role
{
    protected $name;

    public function __construct($name)
    {
        $this->name = (string) $name;
    }

    public function getName()
    {
        return "User " . $this->name;
    }
}
// 组类型的对象结构
class Group extends Role
{
    protected $name;

    public function __construct($name)
    {
        $this->name = (string) $name;
    }

    public function getName()
    {
        return "Group: " . $this->name;
    }
}
// 客户端调用
$visitor = new RolePrintVisitor();
$role = new User("Farll");
$role->accept($visitor);

会输出: Role: User Farll

Leave a Reply

Your email address will not be published. Required fields are marked *