面向对象的洗礼:设计模式(二十八)之访问者模式
访问者模式,是设计模式中最难的一种模式。访问者模式适用于数据结构相对稳定的系统。访问者模式对数据结构和作用于结构上的操作之间进行了一次解耦合。访问者模式的目的是把处理从数据结构分离出来。
访问者模式的适用场景:所开发的系统具有比较问题的数据结构,又有抑郁变化的算法。因为访问者模式使得算法操作的增加和扩展变得容易。优点是增加新的操作更加容易,因为增加新操作就是意味着增加新的访问者,访问者模式将有关的行为集中到一个对象中。缺点显而易见了,就是改变数据结构变得下个对困难。
访问者模式:表示一个作用于某对象的结构中的各个元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。
场景:人类分为男女,对于人类这个系统,分类是非常固定的,一个元素是男,一个元素是女(人妖滚粗)。男女对同一件事情往往有不同的观点。以PHP为代码环境,模拟设计模式之访问者模式的代码实现。(暂时没有想到好的例子,就从《大话设计模式》中访问者模式摘了一段)
<?php class Action{ public function getManView(Man $manObj){} public function getWomanView(Woman $manObj){} } class Person{ public function accept(Action $actionObj){} } class Success extends Action{ public function getManView(Man $manObj){ echo sprintf('%s成功时,背后多半有一个伟大的女人<br>', $manObj->getName()); } public function getWomanView(Woman $womanObj){ echo sprintf('%s成功时,背后多半有一个不成功的女人<br>', $womanObj->getName()); } } class Failing extends Action{ public function getManView(Man $manObj){ echo sprintf('%s失败时,闷头喝酒,谁也不用劝<br>', $manObj->getName()); } public function getWomanView(Woman $womanObj){ echo sprintf('%s失败时,眼泪汪汪,谁也劝不住<br>', $womanObj->getName()); } } class Love extends Action{ public function getManView(Man $manObj){ echo sprintf('%s恋爱时,凡事不懂也要装懂<br>', $manObj->getName()); } public function getWomanView(Woman $womanObj){ echo sprintf('%s恋爱时,凡事懂也要装不懂<br>', $womanObj->getName()); } } class Man extends Person{ private $name = '男人'; public function getName(){ return $this->name; } public function accept(Action $actionObj){ $actionObj->getManView($this); } } class Woman extends Person{ private $name = '女人'; public function getName(){ return $this->name; } public function accept(Action $actionObj){ $actionObj->getWomanView($this); } } class ObjectStructure{ private $elementList; public function add(Person $elementObj){ $this->elementList[] = $elementObj; } public function display(Action $visitorObj){ foreach($this->elementList as $element){ $element->accept($visitorObj); } } } //客户端/接口 $o = new ObjectStructure(); $o->add(new Man()); $o->add(new Woman()); $successObj = new Success(); $o->display($successObj); $failingObj = new Failing(); $o->display($failingObj); $loveObj = new Love(); $o->display($loveObj);
这里用到一个双分派技术。客户端将状态(成功、失败、恋爱)作为参数传递给男人,这是第一次分派。男人类调用作为参数的“具体状态中的方法-男人的观点”,同时将自身传递给状态的对象,这是第二次分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。双分派的好处是,如果要增加结婚类,只需要增加如下:
class Marry extends Action{ public function getManView(Man $manObj){ echo sprintf('%s结婚时,有妻徒刑<br>', $manObj->getName()); } public function getWomanView(Woman $womanObj){ echo sprintf('%s结婚时,婚姻保险<br>', $womanObj->getName()); } }
除此之外,客户端在需要的时候调用即可。不需要动其他的代码,增加新算法,只需要扩展一个新类。完美体现开放-封闭原则。