浅谈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生成数组再传给js的方法
Aug 07 PHP
ThinkPHP表单自动验证实例
Oct 13 PHP
调试PHP程序的多种方法介绍
Nov 06 PHP
PHP使用ffmpeg给视频增加字幕显示的方法
Mar 12 PHP
PHP实现事件机制实例分析
Jun 26 PHP
php实现简单爬虫的开发
Mar 28 PHP
Zend Framework过滤器Zend_Filter用法详解
Dec 09 PHP
php实现购物车产品删除功能(2)
Jul 23 PHP
PHP实现简单计算器小程序
Aug 28 PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
Apr 27 PHP
Yii 框架使用数据库(databases)的方法示例
May 19 PHP
php中配置文件保存修改操作 如config.php文件的读取修改等操作
May 12 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
php木马webshell扫描器代码
2012/01/25 PHP
JQuery 入门实例1
2009/06/25 Javascript
JS验证邮箱格式是否正确的代码
2013/12/05 Javascript
关于JavaScript中name的意义冲突示例介绍
2014/05/29 Javascript
浅谈JS的基础类型与引用类型
2016/09/13 Javascript
浅谈javascript中的 “ && ” 和 “ || ”
2017/02/02 Javascript
JavaScript实现一个空中避难的小游戏
2017/06/06 Javascript
vue.js的手脚架vue-cli项目搭建的步骤
2017/08/30 Javascript
Angular4 组件通讯方法大全(推荐)
2018/07/12 Javascript
[58:46]OG vs NAVI 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python调用java模块SmartXLS和jpype修改excel文件的方法
2015/04/28 Python
Python3中的真除和Floor除法用法分析
2016/03/16 Python
jupyter notebook引用from pyecharts.charts import Bar运行报错
2020/04/23 Python
Python 在字符串中加入变量的实例讲解
2018/05/02 Python
python实现图片文件批量重命名
2020/03/23 Python
python tkinter界面居中显示的方法
2018/10/11 Python
python实现微信每日一句自动发送给喜欢的人
2019/04/29 Python
python实现截取屏幕保存文件,删除N天前截图的例子
2019/08/27 Python
Python使用random模块生成随机数操作实例详解
2019/09/17 Python
python使用openpyxl操作excel的方法步骤
2020/05/28 Python
在keras中实现查看其训练loss值
2020/06/16 Python
探讨HTML5移动开发的几大特性(必看)
2015/12/30 HTML / CSS
10条PHP编程习惯
2014/05/26 面试题
生态学毕业生自荐信
2013/10/27 职场文书
旷课检讨书1000字
2014/02/14 职场文书
机械制造毕业生求职信
2014/03/03 职场文书
募捐倡议书怎么写
2014/05/14 职场文书
党的群众路线教育实践活动个人自我剖析材料
2014/10/07 职场文书
整改报告格式
2014/11/06 职场文书
毕业生班级鉴定评语
2015/01/04 职场文书
爱心捐助活动总结
2015/05/09 职场文书
小学教师教育随笔
2015/08/14 职场文书
关于springboot 配置date字段返回时间戳的问题
2021/07/25 Java/Android
python中super()函数的理解与基本使用
2021/08/30 Python
python_tkinter弹出对话框创建
2022/03/20 Python
html中相对位置与绝对位置的具体使用
2022/05/15 HTML / CSS