DEV Community

菜皮日记
菜皮日记

Posted on

行为型设计模式-访问器 Visitor

简介

在不改变原有类的前提下,可以新增不同的访问器,每种访问器都可自定义访问策略。

如超市购物车里什么物品都有,顾客遍历访问商品主要查看商品是否过期,包装是否完整,收银员遍历商品主要查看价格。顾客和收银员可以理解为两种Visitor,各自关注的重点就是Visitor中的visit方法实现逻辑。

原始数据结构就像数据库,而访问器就像基于该数据库的业务逻辑,不同访问器按照不同业务规则使用数据。

角色

  • 抽象Visitor

    定义访问每一种Element的方法签名visit

  • 具体Visitor

    实现具体访问方法visit

  • 抽象Element

    定义accept方法,支持将visitor做参数,通过调用accept方法,实现accept→visitor→visit的调用

  • 具体Element

    实现具体方法

类图

如图,Element中的accept方法以Visitor做参数,其中调用visitor.visit方法,将主动权交给Visitor去处理。实现每新增一种visitor,对原Element无感知

类图

代码


interface Component
{
    public function accept(Visitor $visitor): void;
}

class ConcreteComponentA implements Component
{
    public function accept(Visitor $visitor): void
    {
        $visitor->visitConcreteComponentA($this);
    }

    public function exclusiveMethodOfConcreteComponentA(): string
    {
        return "A";
    }
}

class ConcreteComponentB implements Component
{
    public function accept(Visitor $visitor): void
    {
        $visitor->visitConcreteComponentB($this);
    }

    public function specialMethodOfConcreteComponentB(): string
    {
        return "B";
    }
}

interface Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void;

    public function visitConcreteComponentB(ConcreteComponentB $element): void;
}

class ConcreteVisitor1 implements Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void
    {
        echo $element->exclusiveMethodOfConcreteComponentA() . " + ConcreteVisitor1\n";
    }

    public function visitConcreteComponentB(ConcreteComponentB $element): void
    {
        echo $element->specialMethodOfConcreteComponentB() . " + ConcreteVisitor1\n";
    }
}

class ConcreteVisitor2 implements Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void
    {
        echo $element->exclusiveMethodOfConcreteComponentA() . " + ConcreteVisitor2\n";
    }

    public function visitConcreteComponentB(ConcreteComponentB $element): void
    {
        echo $element->specialMethodOfConcreteComponentB() . " + ConcreteVisitor2\n";
    }
}

function clientCode(array $components, Visitor $visitor)
{
    foreach ($components as $component) {
        $component->accept($visitor);
    }
}

$components = [
    new ConcreteComponentA(),
    new ConcreteComponentB(),
];

echo "The client code works with all visitors via the base Visitor interface:\n";
$visitor1 = new ConcreteVisitor1();
clientCode($components, $visitor1);

echo "It allows the same client code to work with different types of visitors:\n";
$visitor2 = new ConcreteVisitor2();
clientCode($components, $visitor2);
Enter fullscreen mode Exit fullscreen mode

output:

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1
It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
Enter fullscreen mode Exit fullscreen mode

Top comments (0)