在访问者模式中使用accept()

时间:2012-01-07 18:18:06

标签: c++ design-patterns

我正在研究使用访客模式。我看到的一些例子建议在每个Element子类中使用accept(Visitor)函数。这个函数的想法是否只是为了访问者可以访问包含多态类型的集合?在这段代码中,我使用访问者进行两种类型的累积,并且它不需要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;
}

这种区别是否正确?也就是说,如果我只计划使用同类容器,我不需要实现接受函数吗?

2 个答案:

答案 0 :(得分:1)

  

这种区别是否正确?也就是说,如果我只打算拥有   同类容器我不需要实现接受函数吗?

是的,这就是模式的本质。

基本上,如果您有一个相对稳定的Element层次结构,它允许您根据需要添加新的Visitor衍生物。然后可以在调用者不知道正在操作的元素的具体类型的情况下调用访问者。

答案 1 :(得分:0)

我所说的不是:

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;
};

?在单个访问者类中有多个重载是否有帮助?或者它只是引入了不必要的依赖?