浅谈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环境配置 php5 mysql5 apache2 phpmyadmin安装与配置
Nov 17 PHP
php curl 上传文件代码实例
Apr 27 PHP
jquery+php实现导出datatables插件数据到excel的方法
Jul 06 PHP
ThinkPHP进程计数类Process用法实例详解
Sep 25 PHP
Yii使用Captcha验证码的方法
Dec 28 PHP
php mysql获取表字段名称和字段信息的三种方法
Nov 13 PHP
php自定义时间转换函数示例
Dec 07 PHP
PHP如何搭建百度Ueditor富文本编辑器
Sep 21 PHP
laravel实现简单用户权限的示例代码
May 28 PHP
PHP操作XML中XPath的应用示例
Jul 04 PHP
thinkphp5框架前后端分离项目实现分页功能的方法分析
Oct 08 PHP
ThinkPHP 框架实现的读取excel导入数据库操作示例
Apr 14 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文件上传(实例)
2013/10/27 PHP
PHP常用数组函数介绍
2014/07/28 PHP
php微信公众平台开发(三)订阅事件处理
2016/12/06 PHP
Laravel 6 将新增为指定队列任务设置中间件的功能
2019/08/06 PHP
laravel框架语言包拓展实现方法分析
2019/11/22 PHP
基于jquery的loading效果实现代码
2010/11/05 Javascript
javaScript同意等待代码实现心得
2011/01/01 Javascript
提交表单时执行func方法实现代码
2013/03/17 Javascript
JS鼠标滑过图片时切换图片实现思路
2013/09/12 Javascript
浅谈jQuery中 wrap() wrapAll() 与 wrapInner()的差异
2014/11/12 Javascript
JavaScript数组对象实现增加一个返回随机元素的方法
2015/07/27 Javascript
基于jquery实现人物头像跟随鼠标转动
2015/08/23 Javascript
js判断checkbox是否选中个数的方法(超简单)
2016/08/19 Javascript
AngularJS通过$location获取及改变当前页面的URL
2016/09/23 Javascript
微信小程序  简单实例(阅读器)的实例开发
2016/09/29 Javascript
解决URL地址中的中文乱码问题的办法
2017/02/10 Javascript
详解Vue2.0之去掉组件click事件的native修饰
2017/04/20 Javascript
JavaScript 中调用 Kotlin 方法实例详解
2017/06/09 Javascript
React BootStrap用户体验框架快速上手
2018/03/06 Javascript
浅谈vue项目利用Hbuilder打包成APP流程,以及遇到的坑
2020/09/12 Javascript
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
2018/11/01 Python
python正则过滤字母、中文、数字及特殊字符方法详解
2020/02/11 Python
Python中SQLite如何使用
2020/05/27 Python
Python爬虫入门有哪些基础知识点
2020/06/02 Python
Css3+Js制作漂亮时钟(附源码)
2013/04/24 HTML / CSS
Html5 canvas画图白板踩坑
2020/06/01 HTML / CSS
东方电视购物:东方CJ
2016/10/12 全球购物
德国最大的服装、鞋子和配件在线商店之一:Outfits24
2019/07/23 全球购物
法国低价在线宠物商店:bitiba.fr
2020/07/03 全球购物
如何用Lucene索引数据库
2016/02/23 面试题
C#面试问题
2016/07/29 面试题
公司周年庆典策划方案
2014/05/17 职场文书
被告答辩状范文
2015/05/22 职场文书
暑期工社会实践报告
2015/07/13 职场文书
浅谈pytorch中的dropout的概率p
2021/05/27 Python
关于使用Redisson订阅数问题
2022/01/18 Redis