Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我们需要访问者模式中的accept(),为什么我们不能直接调用Visitor.visit()?_C++_Design Patterns_Visitor_Double Dispatch - Fatal编程技术网

C++ 为什么我们需要访问者模式中的accept(),为什么我们不能直接调用Visitor.visit()?

C++ 为什么我们需要访问者模式中的accept(),为什么我们不能直接调用Visitor.visit()?,c++,design-patterns,visitor,double-dispatch,C++,Design Patterns,Visitor,Double Dispatch,我正在修改前一段时间使用的访问者模式。我们有一个基类元素,它有一个虚拟方法accept(Visitor),这个方法在从元素继承的所有类中都被重写。accept()在任何派生类中所做的就是调用visitor->visit(*this)。现在,当客户机运行代码时,他/她会执行以下操作,例如: Visitor& theVisitor = *new ConcreteVisitor(); for_each(elements.begin(), elements.end(), [](Eleme

我正在修改前一段时间使用的访问者模式。我们有一个基类元素,它有一个虚拟方法accept(Visitor),这个方法在从元素继承的所有类中都被重写。accept()在任何派生类中所做的就是调用visitor->visit(*this)。现在,当客户机运行代码时,他/她会执行以下操作,例如:

Visitor& theVisitor = *new ConcreteVisitor();    
for_each(elements.begin(), elements.end(), [](Element& e) { e.accept(theVisitor));})
为什么客户端不能像这样调用visitor->visit(元素):

Visitor& theVisitor = *new ConcreteVisitor();    
for_each(elements.begin(), elements.end(), [&theVisitor](Element& e) { theVisitor.visit(e); });
调用element.accept(visitor)中有哪些有用的信息,它依次调用visitor.visit(element)?这使得访问者模式的使用非常麻烦,并且在所有元素类的层次结构中都需要额外的代码


那么,有人能在这里解释一下accept()的好处吗?

我一直对访问者模式感到困惑,我一直试图在互联网上找到解释,这些解释让我更加困惑。现在我意识到了为什么需要访问者模式,以及它的实现方式,所以这里是:

为了解决双重调度问题,需要引入访问者模式

单一分派-当您有一个类层次结构,并且在此层次结构中有一个具体类的实例时 您希望为此实例调用适当的方法。这可以通过函数重写(使用C++中的虚拟函数表)来解决

双重分派是指当您有两个类层次结构,一个层次结构中有一个具体类实例,另一个层次结构中有一个具体类实例,并且您希望调用适当的方法来执行这两个特定实例的工作

让我们看一个例子

第一等级:动物。基础:
动物
,衍生:
哺乳动物
。 第二类层次结构:调用程序。基础:
Invoker
,派生:
MovementInvoker
(移动动物)、
VoiceInvoker
(使动物发声)、
FeedingInvoker
(喂养动物)

现在,对于每个特定的动物和每个特定的调用程序,我们只希望调用一个特定的函数来完成特定的工作(例如,喂鸟或给鱼发声)。所以我们总共有3x3=9个函数来做这些工作

另一件重要的事:运行这9个函数的客户机不想知道他或她手头上有什么具体的
Animal
和什么具体的
Invoker

因此,客户希望执行以下操作:

void act(Animal& animal, Invoker& invoker)
{
  // Do the job for this specific animal using this specific invoker
}
或:

visitor.cpp:

#include <iostream>
#include <memory>
#include <vector>
#include "visitor.h"
using namespace std;

// -----------------------------------------//

void Fish::accept(Invoker& invoker)
{
    invoker.doTheJob(*this);
}

void Mammal::accept(Invoker& invoker)
{
    invoker.doTheJob(*this);
}

void Bird::accept(Invoker& invoker)
{
    invoker.doTheJob(*this);
}

// -----------------------------------------//

void MovementInvoker::doTheJob(Fish& fish)
{
    cout << "Make the fish swim" << endl;
}

void MovementInvoker::doTheJob(Mammal& Mammal)
{
    cout << "Make the mammal run" << endl;
}

void MovementInvoker::doTheJob(Bird& Bird)
{
    cout << "Make the bird fly" << endl;
}

// -----------------------------------------//

void VoiceInvoker::doTheJob(Fish& fish)
{
    cout << "Make the fish keep silence" << endl;
}

void VoiceInvoker::doTheJob(Mammal& Mammal)
{
    cout << "Make the mammal howl" << endl;
}

void VoiceInvoker::doTheJob(Bird& Bird)
{
    cout << "Make the bird chirp" << endl;
}

// -----------------------------------------//

void FeedingInvoker::doTheJob(Fish& fish)
{
    cout << "Give the fish some worms" << endl;
}

void FeedingInvoker::doTheJob(Mammal& Mammal)
{
    cout << "Give the mammal some milk" << endl;
}

void FeedingInvoker::doTheJob(Bird& Bird)
{
    cout << "Give the bird some seed" << endl;
}

int main()
{
    vector<shared_ptr<Animal>> animals = { make_shared<Fish>   (),
                                           make_shared<Mammal> (),
                                           make_shared<Bird>   () };

    vector<shared_ptr<Invoker>> invokers = { make_shared<MovementInvoker> (),
                                             make_shared<VoiceInvoker>    (),
                                             make_shared<FeedingInvoker>  () };

    for(auto& animal : animals)
    {
        for(auto& invoker : invokers)
        {
            animal->accept(*invoker);
        }
    }
}
那么,当客户端获得
Animal
的实例和
Invoker
的实例并调用
Animal.accept(Invoker)
时会发生什么呢

假设
Animal
的实例是
Bird
,而
Invoker
的实例是
FeedingInvoker

然后,由于虚拟函数表
Bird::accept(Invoker&)
将被调用,它将依次运行
Invoker.doTheJob(Bird&)
。 由于
Invoker
实例是
FeedingInvoker
,虚拟函数表将使用
FeedingInvoker::accept(Bird&)
进行此调用

因此,我们进行了双重调度,并为
Bird
FeedingInvoker
调用了正确的方法(9种可能的方法之一)

为什么访客模式好

  • 客户端不需要同时依赖于动物和调用程序的复杂类层次结构

  • 如果需要添加新的具体动物(例如,
    昆虫
    ),则不需要更改现有的
    动物
    层次结构。 我们只需要将:
    doTheJob(昆虫和昆虫)
    添加到
    Invoker
    和所有派生的调用程序

  • 访问者模式优雅地实现了面向对象设计的开放/关闭原则:系统应该对扩展开放,对修改关闭


    (在经典的访问者模式中,
    Invoker
    将被
    Visitor
    替换,而
    doTheJob()
    将被
    visit()
    替换,但对我来说,这些名称实际上并不反映某些工作是在元素上完成的)。

    好处在于信息隐藏,因此元素不需要显示其实现。假设一个元素可能有,也可能没有——这是特定于实现的——有子元素,它们也必须被访问。按照您的想法,元素需要有一个getter方法“getSubElements”,以便您可以访问它们。但是使用前一种思想,accept方法可以被实现来访问它的所有子元素,而不需要透露这个实现细节。好的,你的意思是元素有一些私有方法/变量,Visitor是元素的一个朋友类,所以Visitor可以调用元素的私有方法。但在访问者模式的描述/示例中从未提到过这一点。是的,当然,基本元素类中的Element.accept()可以调用补遗的accept(),但这只是在这里添加了另一种模式,例如模板方法。这不是访问者模式的一部分,这使得它很难理解。不需要调用私有方法(这会破坏信息隐藏)。事实上,为了获得所需的行为,必须重写accept方法。元素声明一个accept方法来接受访问者,并将访问者作为参数。从元素类派生的具体元素实现accept方法。在最简单的形式中,这只不过是对访问者的visit方法的调用。复合元素维护子对象的列表,通常在这些子对象上进行迭代alling每个孩子的accept方法。“好吧,我明白了原因:访问者模式实现了双重分派。因此,应该在第一个类层次结构中调用虚拟方法,在第二个类层次结构中调用虚拟方法。因此,这两个类层次结构的虚拟函数表将用于查找两个具体对象,该方法将为其
    #ifndef __VISITOR__
    #define __VISITOR__
    
    struct Invoker; // forward declaration;
    
    // -----------------------------------------//
    
    struct Animal
    {
        // The name of the function can be anything of course.
        virtual void accept(Invoker& invoker) = 0;
    };
    
    struct Fish : public Animal
    {
        void accept(Invoker& invoker) override;
    };
    
    struct Mammal : public Animal
    {
        void accept(Invoker& invoker) override;
    };
    
    struct Bird : public Animal
    {
        void accept(Invoker& invoker) override;
    };
    
    // -----------------------------------------//
    
    struct Invoker
    {
      virtual void doTheJob(Fish&   fish)   = 0;
      virtual void doTheJob(Mammal& Mammal) = 0;
      virtual void doTheJob(Bird&   Bird)   = 0;
    };
    
    struct MovementInvoker : public Invoker
    {
      void doTheJob(Fish&   fish)   override;
      void doTheJob(Mammal& Mammal) override;
      void doTheJob(Bird&   Bird)   override;
    };
    
    struct VoiceInvoker : public Invoker
    {
      void doTheJob(Fish&   fish)   override;
      void doTheJob(Mammal& Mammal) override;
      void doTheJob(Bird&   Bird)   override;
    };
    
    struct FeedingInvoker : public Invoker
    {
      void doTheJob(Fish&   fish)   override;
      void doTheJob(Mammal& Mammal) override;
      void doTheJob(Bird&   Bird)   override;
    };
    
    #endif
    
    #include <iostream>
    #include <memory>
    #include <vector>
    #include "visitor.h"
    using namespace std;
    
    // -----------------------------------------//
    
    void Fish::accept(Invoker& invoker)
    {
        invoker.doTheJob(*this);
    }
    
    void Mammal::accept(Invoker& invoker)
    {
        invoker.doTheJob(*this);
    }
    
    void Bird::accept(Invoker& invoker)
    {
        invoker.doTheJob(*this);
    }
    
    // -----------------------------------------//
    
    void MovementInvoker::doTheJob(Fish& fish)
    {
        cout << "Make the fish swim" << endl;
    }
    
    void MovementInvoker::doTheJob(Mammal& Mammal)
    {
        cout << "Make the mammal run" << endl;
    }
    
    void MovementInvoker::doTheJob(Bird& Bird)
    {
        cout << "Make the bird fly" << endl;
    }
    
    // -----------------------------------------//
    
    void VoiceInvoker::doTheJob(Fish& fish)
    {
        cout << "Make the fish keep silence" << endl;
    }
    
    void VoiceInvoker::doTheJob(Mammal& Mammal)
    {
        cout << "Make the mammal howl" << endl;
    }
    
    void VoiceInvoker::doTheJob(Bird& Bird)
    {
        cout << "Make the bird chirp" << endl;
    }
    
    // -----------------------------------------//
    
    void FeedingInvoker::doTheJob(Fish& fish)
    {
        cout << "Give the fish some worms" << endl;
    }
    
    void FeedingInvoker::doTheJob(Mammal& Mammal)
    {
        cout << "Give the mammal some milk" << endl;
    }
    
    void FeedingInvoker::doTheJob(Bird& Bird)
    {
        cout << "Give the bird some seed" << endl;
    }
    
    int main()
    {
        vector<shared_ptr<Animal>> animals = { make_shared<Fish>   (),
                                               make_shared<Mammal> (),
                                               make_shared<Bird>   () };
    
        vector<shared_ptr<Invoker>> invokers = { make_shared<MovementInvoker> (),
                                                 make_shared<VoiceInvoker>    (),
                                                 make_shared<FeedingInvoker>  () };
    
        for(auto& animal : animals)
        {
            for(auto& invoker : invokers)
            {
                animal->accept(*invoker);
            }
        }
    }
    
    Make the fish swim
    Make the fish keep silence
    Give the fish some worms
    Make the mammal run
    Make the mammal howl
    Give the mammal some milk
    Make the bird fly
    Make the bird chirp
    Give the bird some seed