浅谈PHP面向对象之访问者模式+组合模式


Posted in PHP onMay 22, 2017

因为原文中延续了组合模式的代码示例来讲访问者模式 所以这里就合并一起来复习了。但主要还是讲访问者模式。顾名思义这个模式会有一个访问者类(就像近期的热播剧“人民的名义”中的检查官,跑到到贪官家里调查取证,查实后就定罪),被访问者类调用访问者类的时候会将自身传递给它使用。

直接看代码:

//被访问者基类

abstract class Unit {
abstract function bombardStrength();//获取单位的攻击力


//这个方法将调用访问者类,并将自身传递给它
function accept(ArmyVisitor $visitor){
$method = "visit" . get_class($this);
$visitor->$method($this);//调用访问者类的方法,这里使用了 "visit" . get_class($this) 组成了方法的名称
}


//按原文的说法是设置一个深度,虽然之后会有调用但这个方法对于理解这个模式不重要可以不用管他(原文示例代码中经常有些跟理解模式原理没太多关系的代码)
protected function setDepth($depth){
$this->depth = $depth;
}

function getDepth(){
return $this->depth;
}
}

 

//弓箭手
class Archer extends Unit{
function bombardStrength(){
return 4;
}
}

//激光炮

class LaserCannonUnit extends Unit{
function bombardStrength(){
return 44;
}
}

//骑兵

class Cavalry extends Unit{
function bombardStrength(){
return 2;//骑兵的攻击力居然比弓箭手低?

}
}

 

//用于组合继承了unit类的实例,并让Army和TroopCarrier类继承removeUnit和addUnit方法,不放基类是因为上述的三个类已经是最小单位了不是一个军事集团removeUnit和addUnit方法对他们没用。

abstract class CompositeUnit extends Unit{
private $units = array();//存放任何继承了unit 类的实例

function getComposite(){ //这个方法主要用于判断当前实例是否是一个 CompositeUnit 类
return $this;
}

protected function units(){
return $this->units;
}

function removeUnit(Unit $unit){//删除一个军事单位
$this->units = array_udiff(
$this->units,array($unit),

function($a,$b){return ($a === $b)?0:1;}

);
}

function addUnit(Unit $unit){//添加一个军事单位
if(in_array($unit,$this->units,true)){
return;
}
$unit->setDepth($this->depth + 1);
$this->units[] = $unit;
}

function bombardStrength(){
$ret = 0;
foreach($this->units as $unit){
$ret +=$unit->bombardStrength();
}
return $ret;
}

function accept(Armyvisitor $visitor){//调用访问者
parent::accept($visitor);//调用基类的accept方法,在第一个客户端代码条用里将会保存军事集团整体的一个信息
foreach($this->units as $thisunit){ //调用军事单位accept方法,在第一个客户端代码条用里将会保存其中每一个军事单位的信息
$thisunit->accept($visitor);
}
}	
}

 

//军队

class Army extends CompositeUnit {

}

//舰队

class TroopCarrier extends CompositeUnit {

}

 

//访问者类

abstract class ArmyVisitor{
abstract function visit(Unit $node);//访问者要执行的业务逻辑
function visitArcher(Archer $node){//其实我觉得对于理解来说这个抽象类有一个抽象方法visit()就够了,原文还多出下面这些方法来绕个圈调用visit

//...... 
$this->visit($node);
}

function visitCavalry(Cavalry $node){

//.......
$this->visit($node);
}

function visitLaserCannonUnit(LaserCannonUnit $node){

//......
$this->visit($node);
}

function visitTroopCarrierUnit(Cavalry $node){

//......
$this->visit($node);
}

function visitArmy(Cavalry $node){

//......
$this->visit($node);
}
}

//这个访问者类主要用于获取并保存被访问者对象的信息
class TextDumpArmyVisitor extends ArmyVisitor {
private $text = "";
function visit(Unit $node){
$ret = "";
$pad = 4 * $node->getDpth();
$ret .= sprintf("%{$pad}s","");
$ret .=get_class($node).": ";
$ret .= "bombard: " . $node->bombardStrength() . "\n";
$this->text .=$ret;
}

function getText(){
return $this->text;
}
}

//用于向每个对象征税的访问者类,客户端代码2中将会调用
class TaxCollectionVisitor extends ArmyVisitor{
private $due=0;
private $report ="";

function visit(Unit $node){
$this->levy($node,1);
}

function visitArcher(Archer $node){//复写了父类的方法,对于不同的单位征收不同的税
$this->levy($node,2);
}

function visitCavalry(Cavalry $node){
$this->levy($node,3);
}

function visitTroopCarrierUnit(TroopCarrierUnit $node){
$this->levy($node,5);
}

private function levy(Unit $unit,$amount){//主要的业务逻辑
$this->report .= "Tax levied for" . get_class($unit);
$this->report .= ": $amount\n";
$this->due +=$amount;
}

function getReport(){
return $this->report;
}

function getTax(){
return $this->due;
}
}


//客户端代码1(获取并输出每个对象的一些信息)
class UnitScript {
static function joinExisting(Unit $newUnit,Unit $occupyingUnit){
$comp;
if(!is_null($com = $occupyingUnit->getComposite())){
$comp->addUnit($newUnit);
} else {
$comp = new Army();
$comp->addUnit($occupyingUnit);
$com->addUnit($newUnit);
}
return $comp;
}
}

 

$main_army = new Army();
UnitScript::joinExisting(new Archer(),$main_army);
UnitScript::joinExisting(new LaserCannonUnit(),$main_army);
UnitScript::joinExisting(new Cavalry(),$main_army);

$textdump = new TextDumpArmyVisitor();
$main_army->accept($textdump);
print $textdump->getText();

 

//客户端代码2(对每个对象征税,最后输出总共征收了多少)
$main_army = new Army();
UnitScript::joinExisting(new Archer(),$main_army);
UnitScript::joinExisting(new LaserCannonUnit(),$main_army);
UnitScript::joinExisting(new Cavalry(),$main_army);
$taxcollector = new TaxCollectionVisitor();
$main_army->accept($taxcollector);
print $taxcollector->getTax();

//上述的代码因为太懒没测试,抱歉! 感兴趣的朋友就自己运行调试一下吧!

以上这篇浅谈PHP面向对象之访问者模式+组合模式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
利用PHP动态生成VRML网页
Oct 09 PHP
php 多线程上下文中安全写文件实现代码
Dec 28 PHP
php 判断是否是中文/英文/数字示例代码
Sep 30 PHP
php中strtotime函数用法详解
Nov 15 PHP
PHP文件生成的图片无法使用CDN缓存的解决方法
Jun 20 PHP
Laravel中Trait的用法实例详解
Mar 16 PHP
php flush无效,IIS7下php实时输出的方法
Aug 25 PHP
关于PHP内置的字符串处理函数详解
Feb 04 PHP
PHP实现的各类hash算法长度及性能测试实例
Aug 27 PHP
PHP使用XMLWriter读写xml文件操作详解
Jul 31 PHP
java解析json方法总结
May 16 PHP
php探针使用原理和技巧讲解
Sep 17 PHP
php readfile下载大文件失败的解决方法
May 22 #PHP
老生常谈PHP 文件写入和读取(必看篇)
May 22 #PHP
PHP中trait使用方法详细介绍
May 21 #PHP
php写app接口并返回json数据的实例(分享)
May 20 #PHP
PHP实现json_decode不转义中文的方法
May 20 #PHP
Yii框架参数化查询中IN查询只能查询一个的解决方法
May 20 #PHP
Yii框架使用魔术方法实现跨文件调用功能示例
May 20 #PHP
You might like
一个颜色轮换的简单例子
2006/10/09 PHP
PHP命名空间(namespace)的使用基础及示例
2014/08/18 PHP
PHP中imagick函数的中文解释
2015/01/21 PHP
php四种定界符详解
2017/02/16 PHP
PHP实现获取ip地址的5种方法,以及插入用户登录日志操作示例
2019/02/28 PHP
tp5 sum某个字段相加得到总数的例子
2019/10/18 PHP
JavaScript CSS修改学习第二章 样式
2010/02/19 Javascript
情人节专属 纯js脚本1k大小的3D玫瑰效果
2012/02/11 Javascript
使用jQuery清空file文件域的解决方案
2013/04/12 Javascript
将两个div左右并列显示并实现点击标题切换内容
2013/10/22 Javascript
jquery插件开发之实现md5插件
2014/03/17 Javascript
在Ubuntu上安装最新版本的Node.js
2014/07/14 Javascript
js实现绿白相间竖向网页百叶窗动画切换效果
2015/03/02 Javascript
深入探究使JavaScript动画流畅的一些方法
2015/06/30 Javascript
jfinal与bootstrap的登录跳转实战演习
2015/09/22 Javascript
js创建jsonArray传输至后台及后台全面解析
2016/04/11 Javascript
JavaScript中有关一个数组中最大值和最小值及它们的下表的输出的解决办法
2016/07/01 Javascript
jQuery自适应轮播图插件Swiper用法示例
2016/08/24 Javascript
javascript使用递归算法求两个数字组合功能示例
2017/01/03 Javascript
JS实现的判断方法、变量是否存在功能示例
2020/03/28 Javascript
Vue 实现展开折叠效果的示例代码
2018/08/27 Javascript
手挽手带你学React之React-router4.x的使用
2019/02/14 Javascript
Electron+vue从零开始打造一个本地播放器的方法示例
2020/10/27 Javascript
[03:57]2016完美“圣”典风云人物:rOtk专访
2016/12/09 DOTA
Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
2008/09/06 Python
在Django的模型中添加自定义方法的示例
2015/07/21 Python
Python中selenium实现文件上传所有方法整理总结
2017/04/01 Python
Python2和Python3中print的用法示例总结
2017/10/25 Python
Centos7 下安装最新的python3.8
2019/10/28 Python
使用Python合成图片的实现代码(图片添加个性化文本,图片上叠加其他图片)
2020/04/30 Python
彼得罗夫美国官网:Peter Thomas Roth美国(青瓜面膜)
2017/11/05 全球购物
巴西最大的巴士票务门户:Quero Passagem
2020/11/21 全球购物
优纳科技软件测试面试题
2012/05/15 面试题
行政经理岗位职责
2013/11/09 职场文书
使用Oracle命令进行数据库备份与还原
2021/12/06 Oracle
SQL Server中T-SQL标识符介绍与无排序生成序号的方法
2022/05/25 SQL Server