Reputation: 12227
Composite pattern is often used with visitor. I am trying to figure out what exactly goes into composite and what in visitor. For example if one of the composite has unique property/attribute, will it still be stored in it and visitor will only dig it out or visitor will keep it?
I have written a quick demo to illustrate the problem (note: I have minimized code).
using namespace std;
class Visitor
{
public:
virtual string visit(class Manager * manager) = 0;
virtual string visit(class SalesPerson * salesPerson) = 0;
};
class Employee
{
public:
virtual void add(Employee * employee) = 0;
virtual void remove(Employee * employee) = 0;
virtual string name() = 0;
virtual string department() = 0;
virtual int salary() = 0;
virtual void awardBonus(int amount) = 0;
virtual void accept(Visitor * v) = 0;
protected:
string m_name;
string m_dept;
int m_salary;
};
class Manager : public Employee
{
protected:
QList <Employee *> subordinates;
virtual void add(Employee * employee)
{
subordinates.append( employee );
}
virtual void remove(Employee * employee) {};
virtual string name() { return m_name; };
virtual string department() { return m_dept; };
virtual int salary() { return m_salary; };
virtual void awardBonus(int amount) {};
void accept(Visitor *v)
{
v->visit(this);
}
};
class SalesPerson: public Employee
{
public:
float commision; // sales employee gets commision
string territory;
SalesPerson(): territory("Unknown") {};
void accept(Visitor *v)
{
v->visit(this);
}
virtual void add(Employee * employee) {};
virtual void remove(Employee * employee) {};
virtual string name() { return m_name; };
virtual string department() { return m_dept; };
virtual int salary() { return m_salary; };
virtual void awardBonus(int amount) {};
};
class AwardStockOptionsVisitor : public Visitor
{
public:
int shares;
string visit(Manager *manager)
{
shares = 200;
}
string visit(SalesPerson *salesPerson)
{
shares = 100;
}
};
class GetTerritoryVisitor : public Visitor
{
public:
string territory;
string visit(Manager *manager)
{
return "";
}
string visit(SalesPerson *salesPerson)
{
territory = salesPerson->territory;
return salesPerson->territory;
}
};
int main(int argc, char *argv[])
{
Employee * manager = new Manager;
Employee * salesPerson = new SalesPerson;
GetTerritoryVisitor * getTerritory = new GetTerritoryVisitor;
salesPerson->accept( getTerritory );
cout << "Sales territory is " << getTerritory->territory << endl;
manager->add( salesPerson );
}
In this example, an employee can have a lot of attributes, some of them differ from one another depending on what kind of employee it is.
For example SalesPerson has a territory attribute which is not applicable to other employees, does the composite still store it and visitor only retrieves its value? In that case will we need two visitors to setValue()
and getValue()
if I need these operations? The 2nd paradigm is should the visitor instead store this attribute, in essence add this property which would otherwise not exist in employee?
How about stock options property which is lets assume is given only to 5% employees. Should this be stored in composite and extended by visitor?
One of my concern is employee composite can have many additional attributes which could also mean differences widening up in derived class. Is it okay if composite has relatively large attributes that doesn't actually apply to all sub classes? Should we add property via visitor or only use visitor to provide interface to the specific additional properties that a unique subclass may have?
Upvotes: 0
Views: 1716
Reputation: 16172
The problem is that returning something from the Visitor
's visit
method will not really work:
class GetTerritoryVisitor : public Visitor
{
public:
string territory;
string visit(Manager *manager)
{
return ""; // who will use this?
}
string visit(SalesPerson *salesPerson)
{
territory = salesPerson->territory; // this will be overwritten
return salesPerson->territory; // and who will use this?
}
};
The way Visitor
+ Composite
works is this (pseudo-code):
class Composite {
function accept(Visitor visitor) {
foreach (element in this->getElements()) {
element->accept(visitor);
}
this->accept(visitor);
}
}
Visitor visitor = new GetTerritoryVisitor();
composite->accept(visitor);
Keep in mind that in general case the visitor
object will visit many objects. So there is no way to "capture" the value returned from its visit
method.
Also if you save the individual result in the property, like you do in GetTerritoryVisitor
, it will not work as well, because it will be overwritten by each SalesPerson
you visit.
Usually Visitor
does something with the object or accumulates some information.
So I could imagine the visitor which creates a list of territories of sales mans:
class GetTerritoryVisitor : public Visitor {
list territories;
string visit(SalesPerson *salesPerson) {
this->territories->add(salesPerson->territory);
}
list getTerritories() {
return this->territories;
}
};
Visitor visitor = new GetTerritoryVisitor();
composite->accept(visitor);
// now we have a list of territories of all sales mans
// and can use it for something
allTerritories = visitor->getTerritories();
Upvotes: 2
Reputation: 73406
While both design patterns are complementary, their intent is different:
The composite is a structural pattern. The intent is to represent hierarchies and tread individual objects and composition of objects uniformly.
The visitor is a behavioural pattern. The intent is to represent an operation to be performed on the elements of an objective structure.
According to this principle, the visitor is not there to store persistent data. So, yes, any attributes that might be specific to a category of employees shall remain in the composite.
The fact that you have large sets of attributes (wheteher specific to one employee derivate or common to all) doesn't change the principles. The visitor pattern is intended to cope with different kind of elements in the composite, with the use of different visitinging member functions (one for each different class).
The only inconvenience, is when you add new classes to your composite structure, you'd need to revise your visitors accordingly.
You would put some values in the visitor, only temporarily, to perform an operation. For example:
Implementation questions:
Your implementation of the visitor pattern is incomplete. You have no visiting logic that goes through composite structure.
Looking at the way you intend to use your visitor, I wonder if you are not looking for adding attributes/functions to objects, instead of browsing through them.
I suggest you have a look at the decorator pattern, which is better suited to this kind of usage: it's a structural pattern, that extends an existing family of classes.
Upvotes: 2