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

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay