在全部設計模式中,訪問者模式算得上比較難理解的一種設計模式。雖然這種模式比較難理解,可是也須要去知道這種模式具體是怎麼回事兒,我將從最簡單的代碼講起,嘗試去說說這種模式的由來。設計模式
先給定一個場景:顧客去商店買物品,購物結束後須要在收銀員進行結帳。this
這裏具體羅列一下幾個關鍵詞:顧客(client)、物品(goods)和收銀員(cashier)設計
因而就有如下代碼:code
class Cashier { public function checkGoods(Goods $goods) { echo "【Goods】Title: {$goods->title}, price: {$goods->price}"; } } class Goods { public $title = ''; public $price = 0; public function __construct($title, $price) { $this->title = $title; $this->price = $price; } } /* client */ $pencil = new Goods('鉛筆', 2.5); $cashier = new Cashier(); $cashier->checkGoods($pencil);
整體解釋:繼承
以上沒問題,不少相似的項目也是這樣設計的。宏觀來講 cashier 做爲服務去結帳是沒有問題的,畢竟收銀員就是給客戶結帳的,其實這裏有點相似外觀模式(Facade Pattern)。接口
那麼有沒有辦法換一種思路去設計呢?看下面的代碼:ci
class Cashier { public function checkGoods(Goods $goods) { echo "【Goods】Title: {$goods->title}, price: {$goods->price}"; } } class Goods { public $title = ''; public $price = 0; public function __construct($title, $price) { $this->title = $title; $this->price = $price; } public function checkSelf(Cashier $cashier) { $cashier->checkGoods($this); } } $pencil = new Goods('鉛筆', 2.5); $cashier = new Cashier(); $pencil->checkSelf($cashier);
以上代碼在 Goods 中加了一個 checkSelf 方法用於接收 Cashier 實例,在 checkSelf 的這個方法中,咱們調用了實例的 checkGoods 方法。改寫 client 端的代碼,將不在使用 checkGoods 方法進行檢查,而使用的是 checkSelf 方法將 cashier 傳入 Goods 中。element
比較以上兩段代碼的區別,體會思惟方式,這就是訪問者模式的雛形。it
接下來就是抽象了。把實例抽象化,該用接口的用接口,該用繼承的用繼承。io
如下就是改寫上面的例子
interface IVisitor { public function visit(IElement $element); } interface IElement { public function accept(IVisitor $visitor); } class Cashier implements IVisitor { public function visit(IElement $element) { echo "【Goods】Title: {$element->title}, price: {$element->price}"; } } class Goods implements IElement { public $title = ''; public $price = 0; public function __construct($title, $price) { $this->title = $title; $this->price = $price; } public function accept(IVisitor $visitor) { $visitor->visit($this); } } $pencil = new Goods('鉛筆', 2.5); $cashier = new Cashier(); $pencil->accept($cashier);
這樣看起來是否是可以理解很多呢?
其實設計模式沒那麼複雜,大部分是一種思惟方式的轉變。