Подтвердить что ты не робот

Объяснение шаблона посетителя

Итак, я прочитал всю документацию о шаблоне посетителя, и я все еще сильно запутался. Я взял этот пример из другого вопроса, может ли кто-нибудь помочь мне понять? Например, когда мы используем шаблон дизайна посетителя? Я думаю, что я, возможно, понял некоторые из них, но я просто не могу видеть большую картину. Как узнать, когда я могу его использовать?

class equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor) = 0;
}

class floppyDisk : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class processor : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class computer : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class equipmentVisitor
{
  virtual void visitFloppyDisk(floppyDisk* );
  virtual void visitProcessor(processor* );
  virtual void visitComputer(computer* );
}

// Some additional classes inheriting from equipmentVisitor would be here

equipmentVisited* visited;
equipmentVisitor* visitor;

// Here you initialise visited and visitor in any convenient way

visited->accept(visitor);
4b9b3361

Ответ 1

Шаблон посетителя используется для реализации двойной отправки. Простыми словами это означает, что исполняемый код зависит от типов среды выполнения двух объектов.

Когда вы вызываете обычную виртуальную функцию, это отдельная отправка: кусок кода, который запускается, зависит от типа среды выполнения одного объекта, а именно от того, какой виртуальный метод вы вызываете.

С шаблоном посетителя метод, который вызывается в конечном счете, зависит от типа двух объектов - типа объекта, реализующего equipmentVisitor, и типа объекта, на который вы вызываете accept (т.е. equipmentVisited).

Существуют другие способы реализации двойной отправки на С++. Пункт 31 Скотта Мейера "Более эффективный С++" глубоко изучает этот вопрос.

Ответ 2

Я думаю, что имя шаблона Visitor довольно неудачно. Вместо слова "посетитель" я бы сказал "Functor" или "Operator", а вместо "visit" я бы сказал "apply".

Мое понимание шаблона посетителя выглядит следующим образом:

В метапрограмме шаблонов (STL/BOOST) (привязка времени компиляции) вы можете достичь (ортогональный дизайн) разделение операций от структур, с помощью функциональных объектов (Функторы.) Например, в

template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

comp является функтором/оператором, представляющим операцию "меньше" в очень общем виде, поэтому вам не нужно иметь много вариантов функции сортировки:

Для шаблона посетителя вы хотите добиться чего-то подобного, но в случае времени выполнения (позднего) привязки:

Вы хотите упростить интерфейс A, вы хотите сохранить возможность для будущих расширений (новые операции, работающие с A), и вы хотите добиться стабильности интерфейса A в случае этих расширений.

Из исходного "жирного" класса:

class A
{ 
  public:
    virtual void function_or_operation_1();//this can be implemented in terms of public interface of the other functions
    virtual void function_or_operation_2();
    //..etc

    virtual void function_or_operation_N();
  public:
    //stable public interface, some functions of procedures

  private:
  //....
}

вы удаляете как можно больше функций из открытого интерфейса (если они могут быть реализованы с точки зрения неисследованных функций одного и того же публичного интерфейса) и представляют операции как объекты-объекты-объекты или объекты из новой иерархии Functor:

Вы уменьшаете количество функций в базовом классе A, имея очень общий интерфейс, используя forward объявленный Functor_or_Operator:

 class Functor_or_Operator;
 class A
 {
   public:
     virtual void apply(Functor_or_Operator*);//some generic function operates on this objects from A hierarchy 
   //..etc
   public:
     //stable public interface, some functions

   private: 
     //....
 }

//Теперь у вас есть классы N (= 3) в иерархии A (A, B, C) и M или функции, представленные классами в иерархии Functor_or_Operator Вам нужно реализовать определения N * M о том, как каждая операция из Functor_or_Operator работает над каждым классом в иерархии A. Самое главное, вы можете это сделать, не меняя интерфейса класса "А". Объявление класса "А" становится очень стабильным в случае новых дополнений при введении новых операций или функций, работающих с объектами иерархии А или в случае новых производных классов в иерархии А. Стабильность A (без изменений в A) при наличии дополнений важна, чтобы избежать дорогостоящей (а иногда и невозможной) перекомпиляции программного обеспечения, которое включает заголовки A во многих местах.

Для каждого нового класса в иерархии A вы расширяете определение базы Functor_or_Operator, добавляете новые файлы реализации, но вам не нужно касаться заголовка базового класса A (обычно интерфейса или абстрактного класса).

  class Functor_or_Operator 
  {
    virtual void apply(A*)=0;
    virtual void apply(B*)=0;
    virtual void apply(C*)=0;
  }

  void A::apply(Functor_or_Operator* f) 
  { f->apply(this);} //you need this only if A is not abstract (it is instantiable)

  class B:public A
  {
    public:
     void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymhorphic Functor f on this object
     //..the rest of B implementation.
  }

  class C:public A
  {
    public:
     void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymorfic Functor f on this object
    //..the rest of C implementation.
  }

  class Functor_or_Operator_1:public Functor_or_Operator
  {
     public:
        //implementations of application of a function represented by Functor_or_Operator_1 on each A,B,C
        void apply(A*) {}//( only if A is instantiable,not an abstract class)
        void apply(B*) {}
        void apply(C*) {}
  }

  class Functor_or_Operator_2:public Functor_or_Operator
  {
    public:
      //implementations of application of a function represented by Functor_or_Operator_2 on each A,B,C
       void apply(A*) {}//( only if A is instantiable,not an abstract class)
       void apply(B*) {}
       void apply(C*) {}
  }