【PHP设计模式】行为型之访问者(Vistor)

  访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。 访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

  一组继承于某个基类的子类集合或者数组就是对象结构。为一组对象,即对象结构提供批量动态添加及访问功能的模式。

<UML>


具体的被访问者中决定调用访问者类中的具体方法。指定引用访问者的方法来实现功能。

<示例一>

class UnitException extends Exception {}
/**
 * 访问者
 */
abstract class ArmyVisitor  {
    abstract function visit( Unit $node );

    function visitArcher( Archer $node ) {
        $this->visit( $node );
    }
    function visitCavalry( Cavalry $node ) {
        $this->visit( $node );
    }
    function visitLaserCanonUnit( LaserCanonUnit $node ) {
        $this->visit( $node );
    }

    function visitTroopCarrierUnit( TroopCarrierUnit $node ) {
        $this->visit( $node );
    }
    function visitArmy( Army $node ) {
        $this->visit( $node );
    }
}
//文本记录信息
class TextDumpArmyVisitor extends ArmyVisitor {
    private $text="";

    function visit( Unit $node ) {
//        $ret = "";
//        $pad = 4*$node->getDepth();
//        $ret .= sprintf( "%{$pad}s", "" );
//        $ret .= get_class($node).": ";
        $ret .= "bombard: ".$node->bombardStrength()."<br>";
        $this->text .= $ret;
    }
       function visitTroopCarrierUnit( TroopCarrierUnit $node ) {
        $this->text .= "Free_TroopCarrier";
    }
    function getText() {
        return $this->text;
    }
}
//税收访问者
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 .= "指定兵种为  ".get_class( $unit )."  税收为";
        $this->report .= "  $amount<br>";//记录
        $this->due += $amount;//应付税
    }
    function getReport() {
        return $this->report;
    }
    function getTax() {
        return $this->due;
    }
}

/**
 * 被访问者
 */
abstract class Unit {
    protected $depth = 0;

    function getComposite() {
        return null;
    }

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

    abstract function bombardStrength();

    function accept( ArmyVisitor $visitor ) {
        $method = "visit".get_class( $this );
        //echo $method."<br>";
        $visitor->$method( $this );//visitArcher(new Archer());
    }
}
//单独的对象
class Archer extends Unit {//弓箭手
    function bombardStrength() {
        return 4;
    }
}
class Cavalry extends Unit {//骑兵
    function bombardStrength() {
        return 2;
    }
}
class LaserCanonUnit extends Unit {//激光炮
    function bombardStrength() {
        return 44;
    }
}
/**
 * 组合对象
 */
abstract class CompositeUnit extends Unit {
    private $units = array();

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

    function addUnit( Unit $unit ) {//增加单位
        foreach ( $this->units as $thisunit ) {
            if ( $unit === $thisunit ) {
                return;
            }
        }
        $unit->setDepth($this->depth+1);
        $this->units[] = $unit;
    }
    function removeUnit( Unit $unit ) {//减少单位
        $units = array();
        foreach ( $this->units as $thisunit ) {
            if ( $unit !== $thisunit ) {
                $units[] = $thisunit;
            }
        }
        $this->units = $units;
    }
/*
    function accept( ArmyVisitor $visitor ) {
        $method = "visit".get_class( $this );
        $visitor->$method( $this );
        foreach ( $this->units as $thisunit ) {
            $thisunit->accept( $visitor );
        }
    }
*/
    //组合模式接受访问者,调用访问者的"visit".get_class( $this )( $this );方法
    function accept( ArmyVisitor $visitor ) {
        parent::accept( $visitor );//当前组合对象调用正确的访问者方法
        foreach ( $this->units as $thisunit ) {
            $thisunit->accept( $visitor );//将访问者对象传递给每一个组合对象的子对象
        }
    }
}
//军队组合对象
class Army extends CompositeUnit {

    function bombardStrength() {
        $ret = 0;
        foreach( $this->units() as $unit ) {
            $ret += $unit->bombardStrength();
        }
        return $ret;
    }
}
//运兵船组合对象
class TroopCarrierUnit extends CompositeUnit {

    function addUnit( Unit $unit ) {//运兵船不能添加骑兵
        if ( $unit instanceof Cavalry ) {
            throw new UnitException("Can't get a horse on the vehicle");
        }
        parent::addUnit( $unit );
    }

    function bombardStrength() {//运兵过程中攻击强度为0
        return 0;
    }
}

$main_army = new Army();
$main_army->addUnit( new Archer() );
$main_army->addUnit( new LaserCanonUnit() );
$main_army->addUnit( new Cavalry() );

$sub_army = new TroopCarrierUnit();
$sub_army->addUnit( new Archer() );
$sub_army->addUnit( new LaserCanonUnit() );

$main_army->addUnit($sub_army);

$textdump = new TextDumpArmyVisitor();
$main_army->accept( $textdump  );//记录日志
print $textdump->getText();
$taxcollector = new TaxCollectionVisitor();
$main_army->accept( $taxcollector );
print $taxcollector->getReport();
print "TOTAL: ";
print $taxcollector->getTax()."
";

<结果>

bombard: 50
bombard: 4
bombard: 44
bombard: 2
Free_TroopCarrierbombard: 4
bombard: 44
指定兵种为 Army 税收为 1
指定兵种为 Archer 税收为 2
指定兵种为 LaserCanonUnit 税收为 1
指定兵种为 Cavalry 税收为 3
指定兵种为 TroopCarrierUnit 税收为 5
指定兵种为 Archer 税收为 2
指定兵种为 LaserCanonUnit 税收为 1
TOTAL: 15 

<示例二>

<交互图>


 

/**
 * 访问者
 */
interface Visitor{
    /**访问企业用户相当于给企业客户添加访问者功能*/
    public function visitEnterpriseCustomer(EnterpriseCustomer $ec);
    /**访问普通用户,相当于给普通用户添加访问者功能*/
    public function visitPersonalCustomer(PersonalCustomer $pc);
}

//具体的访问者,实现客户提出服务的请求的功能
class ServiceRequestVisitor implements Visitor{
    //企业用户提出的具体服务请求
    public function visitEnterpriseCustomer(EnterpriseCustomer $ec){
        echo $ec->linkname."处理企业请求<br>";
    }
    //个人用户提出的具体服务请求
    public function visitPersonalCustomer( PersonalCustomer $pc){
        echo $pc->linkname."处理个人请求<br>";
    }
}
/**
 * 被访问者
 */
abstract class Customer{//元素类的抽象方法
    public $customerId,$name;
    //接受访问者访问的方法
    public abstract function accept($vistor);
}
//企业客户
class EnterpriseCustomer extends Customer{
    public $linkname,$linkTelephone,$registerAddress;
    public function accept($visitor){
        $visitor->visitEnterpriseCustomer($this);
    }
}
//个人客户
class PersonalCustomer extends Customer{
    public $telephone,$age;
    public function accept($visitor){
        $visitor->visitPersonalCustomer($this);
    }
}

//对象组合
class ObjectStructure{
    private $col = array();
    //添加被访问者
    public function addElement(Customer $ele){
        array_push($this->col,$ele);
    }
    //执行被访问者接受访问者访问的方法
    public function handleRequest(Visitor $visitor){
        for($i = 0;$i<count($this->col);$i++){
            $this->col[$i]->accept($visitor);//accept方法是调用$visitor里的方法的
        }
    }
}

$os = new ObjectStructure();
//准备一些测试数据,创建客户对象,并加入到os中
$cm1 = new EnterpriseCustomer();
$cm1->linkname = "ABC集团";
$cm2 = new EnterpriseCustomer();
$cm2->linkname = "CDE公司";
$os->addElement($cm1);
$os->addElement($cm2);

$cm3 = new PersonalCustomer();
$cm3->linkname="张三";
$os->addElement($cm3);

//客户提出服务请求,传入服务请求的Visitor
$srVisitor = new ServiceRequestVisitor();
$os->handleRequest($srVisitor);
原文地址:https://www.cnblogs.com/colorstory/p/2664866.html