复合模式通常与访问者一起使用。我试图弄清楚复合材料究竟是什么以及访客中的内容。例如,如果其中一个复合具有唯一的属性/属性,它是否仍会存储在其中,访问者只会将其挖出或访问者保留它?
我已经编写了一个快速演示来说明问题(注意:我已经最小化了代码)。
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 );
}
在此示例中,员工可以拥有许多属性,其中一些属性因其所属的员工类型而异。
例如,SalesPerson的领土属性不适用于其他员工,复合材料是否仍然存储它,访问者只检索其值?在这种情况下,如果我需要这些操作,我们需要两位setValue()
和getValue()
的访问者吗?第二种范例是访客应该存储此属性,实质上是添加此属性,否则员工不存在该属性?
我们假设的股票期权财产仅给予5%的员工。它应该存储在复合中并由访问者扩展吗?
我关注的一个问题是员工组合可以有许多额外的属性,这也可能意味着派生类的差异在扩大。如果复合具有相对较大的属性并且实际上并不适用于所有子类,那还可以吗?我们应该通过访问者添加属性还是仅使用访问者来提供唯一子类可能具有的特定附加属性的接口?
答案 0 :(得分:2)
虽然两种设计模式都是互补的,但它们的意图却不同:
根据这个原则,访问者不存储持久数据。所以,是的,任何可能特定于某类员工的属性都应保留在复合材料中。
您拥有大量属性(特定于一个员工派生或所有员工共同)的事实并未改变原则。访问者模式旨在处理复合中的不同类型的元素,使用不同的访问成员函数(每个不同的类一个)。
唯一的不便是,当您向复合结构中添加新类时,您需要相应地修改访问者。
您只会暂时在访问者中放置一些值来执行操作。例如:
实施问题:
您对访客模式的实施不完整。您没有通过复合结构的访问逻辑。
看看你打算如何使用你的访问者,我想知道你是不是想要为对象添加属性/功能,而不是浏览它们。
我建议您查看decorator pattern,它更适合这种用法:它是一种结构模式,它扩展了现有的类系列。
答案 1 :(得分:2)
问题是从Visitor
visit
方法返回某些内容并不会真正起作用:
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?
}
};
Visitor
+ Composite
的工作方式是这个(伪代码):
class Composite {
function accept(Visitor visitor) {
foreach (element in this->getElements()) {
element->accept(visitor);
}
this->accept(visitor);
}
}
Visitor visitor = new GetTerritoryVisitor();
composite->accept(visitor);
请记住,在一般情况下,visitor
对象将访问许多对象。所以没有办法去捕捉&#34;从visit
方法返回的值。
此外,如果您将个别结果保存在属性中,就像在GetTerritoryVisitor
中一样,它也不会有效,因为您访问的每个SalesPerson
都会覆盖它。
通常Visitor
对对象做某事或积累一些信息。
所以我可以想象创建销售区域列表的访问者:
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();