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

留言

這個網誌中的熱門文章

c語言-關於#define用法

CMD常用網管指令

PHP 與 JavaScript 之間傳值利用 json