Reputation: 501
Currently I am learning about the Visitor Pattern and try out various ideas. Below I have the code of my current setup, which I would like to get functioning somehow.
I would like to have two visitors, one that counts instances of Red
and Blu
separately and one that counts anything (one can assume, it's a Color
)
This is of course solvable by simply implementing the second visitor analogously to the first one, however not using separate variables for counting, but just one. I think however this is unnecessary - if I had for example many, many different colours, the code would be very repetitive: All functions in that visitor would be same, they would simply increment one variable. Surely, there is an easier way, but how? According to the standard Visitor Pattern I have to implement for every color class a visit functions, thus this does not seem to be the right approach.
How would someone solve this problem?
#include <iostream>
class Color
{
public:
virtual void accept(class Visitor*) = 0;
};
class Red: public Color
{
public:
/*virtual*/
void accept(Visitor*);
void eye()
{
std::cout << "Red::eye\n";
}
};
class Blu: public Color
{
public:
/*virtual*/
void accept(Visitor*);
void sky()
{
std::cout << "Blu::sky\n";
}
};
class Visitor
{
public:
virtual void visit(Red*) = 0;
virtual void visit(Blu*) = 0;
};
class CountVisitor: public Visitor
{
public:
CountVisitor()
{
m_num_red = m_num_blu = 0;
}
/*virtual*/
void visit(Red*)
{
++m_num_red;
}
/*virtual*/void visit(Blu*)
{
++m_num_blu;
}
void report_num()
{
std::cout << "Reds " << m_num_red << ", Blus " << m_num_blu << '\n';
}
private:
int m_num_red, m_num_blu;
};
class TemplateVisitor: public Visitor
{
public:
TemplateVisitor() : num_of_colours(0) {}
/*virtual*/
template<class C>
void visit(C* c)
{
++num_of_colours;
}
void report_num()
{
std::cout << "Colours " << num_of_colours << '\n';
}
private:
int num_of_colours;
};
void Red::accept(Visitor *v)
{
v->visit(this);
}
void Blu::accept(Visitor *v)
{
v->visit(this);
}
int main()
{
Color *set[] =
{
new Red, new Blu, new Blu, new Red, new Red, nullptr
};
CountVisitor count_operation;
TemplateVisitor template_visitor;
for (int i = 0; set[i]; i++)
{
set[i]->accept(&count_operation);
set[i]->accept(&template_visitor);
}
count_operation.report_num();
template_visitor.report_num();
}
Upvotes: 0
Views: 66
Reputation: 371
Why not just use a map and add some function to color to use as an identifier?
class Color
{
public:
virtual void accept(class Visitor*) = 0;
virtual std::string color_name() = 0;
};
class Visitor
{
public:
virtual void visit(Color* c);
};
class CountVisitor: public Visitor
{
std::unordered_map<std::string, int> map;
public:
/*virtual*/
void visit(Color* c)
{
map[c.color_name()]++;
}
};
Upvotes: 0
Reputation: 66230
Unfortunately, virtual methods and template methods can't match.
I mean... if your base class Visitor
require
virtual void visit(Red*) = 0;
virtual void visit(Blu*) = 0;
the implementation of two virtual methods in derived classes, you can't solve this obligation with a single template method
template<class C>
void visit(C* c)
{
++num_of_colours;
}
You have to write two methods, absolutely not template, with the exact signature. Maybe adding also override
, to reduce the risk of mistakes.
void visit (Red * r) override
{ ++num_of_colours; }
void visit (Blu * b) override
{ ++num_of_colours; }
Obviously you can define a template method (maybe with another name, but also visit()
if you want) that is called by both virtual overrided methods
template <typename C>
void visit (C * c)
{ ++num_of_colours; }
void visit (Red * r) override
{ visit<Red>(r); }
void visit (Blu * b) override
{ visit<Blu>(b); }
This way, you can implement the logic of the visitor in a single template method and call it by all virtual methods
Upvotes: 1