Использование метода accept () в шаблоне Visitor - PullRequest
3 голосов
/ 07 января 2012

Я изучаю использование шаблона посетителя. В некоторых примерах, которые я вижу, предлагается использовать функцию accept (Visitor) в каждом подклассе Element. Является ли идея этой функции просто так, чтобы посетитель мог посетить коллекцию, которая содержит полиморфные типы? В этом коде я использую посетителя для двух типов накопления, и для него не требуется accept ().

#include <iostream>
#include <vector>

class IntItem
{
public:
  IntItem(const int a) : IntData(a){}

  int IntData;
};

class DoubleItem
{
public:
  DoubleItem(const double a) : DoubleData(a){}

  double DoubleData;
};

class Visitor
{
public:
  virtual void Visit(IntItem &item) = 0;
  virtual void Visit(DoubleItem &item) = 0;
};


class SumAccumulator : public Visitor
{
public:

  SumAccumulator() : Sum(0) {}
  void Visit(IntItem &item)
  {
    Sum += item.IntData;
  }

  void Visit(DoubleItem &item)
  {
    Sum += item.DoubleData;
  }

  double Sum;
};

class AverageAccumulator : public Visitor
{
public:

  AverageAccumulator() : Average(0), Counter(0) {}
  void Visit(IntItem &item)
  {
    Counter++;
    Average = (Counter - 1) * Average + item.IntData;
    Average /= Counter;
  }

  void Visit(DoubleItem &item)
  {
    Counter++;
    Average = (Counter - 1) * Average + item.DoubleData;
    Average /= Counter;
  }

  int Counter;
  double Average;
};

class IntCollection
{
public:
  void Visit(Visitor &visitor)
  {
    for(unsigned int i = 0; i < IntItems.size(); ++i)
      {
      visitor.Visit(IntItems[i]);
      }
  }

  void AddIntItem(const IntItem& item)
  {
    IntItems.push_back(item);
  }

private:
  std::vector<IntItem> IntItems;

};

class DoubleCollection
{
public:
  void Visit(Visitor &visitor)
  {
    for(unsigned int i = 0; i < DoubleItems.size(); ++i)
      {
      visitor.Visit(DoubleItems[i]);
      }
  }

  void AddDoubleItem(const DoubleItem& item)
  {
    DoubleItems.push_back(item);
  }

private:
  std::vector<DoubleItem> DoubleItems;
};

int main(int argc, char *argv[])
{
  /////// Ints ////////
  IntCollection intCollection;
  for(unsigned int i = 0; i < 4; ++i)
    {
    intCollection.AddIntItem(IntItem(i));
    }

  SumAccumulator intSumAccumulator;
  intCollection.Visit(intSumAccumulator);
  std::cout << "int sum: " << intSumAccumulator.Sum << std::endl;

  AverageAccumulator intAverageAccumulator;
  intCollection.Visit(intAverageAccumulator);
  std::cout << "int average: " << intAverageAccumulator.Average << std::endl;

  /////// Doubles ////////
  DoubleCollection doubleCollection;
  for(unsigned int i = 0; i < 4; ++i)
    {
    doubleCollection.AddDoubleItem(DoubleItem(static_cast<double>(i) + .1));
    }
  SumAccumulator doubleSumAccumulator;
  doubleCollection.Visit(doubleSumAccumulator);
  std::cout << "double sum: " << doubleSumAccumulator.Sum << std::endl;

  AverageAccumulator doubleAverageAccumulator;
  doubleCollection.Visit(doubleAverageAccumulator);
  std::cout << "double average: " << doubleAverageAccumulator.Average << std::endl;

  return 0;
}

В этом коде я использую accept (), и единственное отличие состоит в том, что контейнер может содержать объекты разных типов в одном и том же контейнере:

#include <iostream>
#include <string>
#include <vector>

class IntElement;
class DoubleElement;

class Visitor
{
public:
  virtual void visit(IntElement *e) = 0;
  virtual void visit(DoubleElement *e) = 0;
};

class Element
{
public:
  virtual void accept(class Visitor &v) = 0;
};

class IntElement: public Element
{
public:
  IntElement(int i) : IntData(i){}
  /*virtual*/void accept(Visitor &v)
  {
    v.visit(this);
  }

  int IntData;
};

class DoubleElement: public Element
{
public:
  DoubleElement(double d) : DoubleData(d){}
  /*virtual*/void accept(Visitor &v)
  {
    v.visit(this);
  }

  double DoubleData;
};

class SumVisitor: public Visitor
{
public:
  SumVisitor() : Sum(0){}
  /*virtual*/void visit(IntElement *e)
  {
    Sum += e->IntData;
  }
  /*virtual*/void visit(DoubleElement *e)
  {
    Sum += e->DoubleData;
  }

  double Sum;
};

class AverageVisitor: public Visitor
{
public:
  AverageVisitor() : Counter(0) , Average(0){}
  /*virtual*/void visit(IntElement *e)
  {
    Counter++;
    Average = (Counter - 1) * Average + e->IntData;
    Average /= Counter;
  }
  /*virtual*/void visit(DoubleElement *e)
  {
    Counter++;
    Average = (Counter - 1) * Average + e->DoubleData;
    Average /= Counter;
  }
  double Average;
  int Counter;
};

int main()
{
  std::vector<Element*> elements;
  elements.push_back(new IntElement(0));
  elements.push_back(new IntElement(1));
  elements.push_back(new DoubleElement(2));
  elements.push_back(new DoubleElement(3));

  SumVisitor sumVisitor;
  AverageVisitor averageVisitor;
  for (int i = 0; i < elements.size(); i++)
    {
    elements[i]->accept(sumVisitor);
    elements[i]->accept(averageVisitor);
    }
  std::cout << "sum: " << sumVisitor.Sum << std::endl;
  std::cout << "average: " << averageVisitor.Average << std::endl;
}

Правильно ли это различие? То есть, если я планирую иметь только однородные контейнеры, мне не нужно реализовывать функции accept?

Ответы [ 2 ]

1 голос
/ 07 января 2012

Правильно ли это различие? То есть, если я только планирую иметь Однородные контейнеры Мне не нужно реализовывать функции приема?

Да, в этом суть паттерна.

Обычно, если у вас относительно стабильная иерархия Element, она позволяет вам добавлять новые производные Visitor по мере необходимости. После этого посетители могут быть вызваны без того, чтобы тот знал, какой конкретный тип элемента используется.

0 голосов
/ 08 января 2012

То, что я говорю, вместо:

class SumVisitor: public Visitor
{
public:
  SumVisitor() : Sum(0){}
  /*virtual*/void visit(IntElement *e)
  {
    Sum += e->IntData;
  }
  /*virtual*/void visit(DoubleElement *e)
  {
    Sum += e->DoubleData;
  }

  double Sum;
};

Почему бы не иметь:

class IntSumVisitor: public Visitor
{
public:
  SumVisitor() : Sum(0){}
  /*virtual*/void visit(IntElement *e)
  {
    Sum += e->IntData;
  }

  double Sum;
};

class DoubleSumVisitor: public Visitor
{
public:
  DoubleSumVisitor() : Sum(0){}
  /*virtual*/void visit(IntElement *e)
  {
    Sum += e->IntData;
  }

  int Sum;
};

?Помогает ли что-нибудь иметь несколько перегрузок в одном классе посетителя?Или это просто вводит ненужные зависимости?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...