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
也可以這樣理解:現在有一個由許多對象構成的對象結構,這些對象的類都擁有一個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
留言
張貼留言