在全部设计模式中,访问者模式算得上比较难理解的一种设计模式。虽然这种模式比较难理解,可是也须要去知道这种模式具体是怎么回事儿,我将从最简单的代码讲起,尝试去说说这种模式的由来。设计模式
先给定一个场景:顾客去商店买物品,购物结束后须要在收银员进行结帐。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);
这样看起来是否是可以理解很多呢?
其实设计模式没那么复杂,大部分是一种思惟方式的转变。