与访客的复合模式,访客真正去了,怎么样?

时间:2016-02-22 18:47:12

标签: c++ oop design-patterns composite visitor

复合模式通常与访问者一起使用。我试图弄清楚复合材料究竟是什么以及访客中的内容。例如,如果其中一个复合具有唯一的属性/属性,它是否仍会存储在其中,访问者只会将其挖出或访问者保留它?

我已经编写了一个快速演示来说明问题(注意:我已经最小化了代码)。

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%的员工。它应该存储在复合中并由访问者扩展吗?

我关注的一个问题是员工组合可以有许多额外的属性,这也可能意味着派生类的差异在扩大。如果复合具有相对较大的属性并且实际上并不适用于所有子类,那还可以吗?我们应该通过访问者添加属性还是仅使用访问者来提供唯一子类可能具有的特定附加属性的接口?

2 个答案:

答案 0 :(得分:2)

虽然两种设计模式都是互补的,但它们的意图却不同:

  • composite是一种结构模式。目的是表示层次结构并统一地处理单个对象和对象的组合。

  • visitor是一种行为模式。目的是表示要对目标结构的元素执行的操作。

根据这个原则,访问者不存储持久数据。所以,是的,任何可能特定于某类员工的属性都应保留在复合材料中。

您拥有大量属性(特定于一个员工派生或所有员工共同)的事实并未改变原则。访问者模式旨在处理复合中的不同类型的元素,使用不同的访问成员函数(每个不同的类一个)。

唯一的不便是,当您向复合结构中添加新类时,您需要相应地修改访问者。

您只会暂时在访问者中放置一些值来执行操作。例如:

  • 你可以有一个机会来统计经理以下的人数和他们的总薪水
  • 或者您可以拥有访问者的共享总数,这会阻碍算法在员工上分层分配这些共享。在分发结束时,访问者留下的份额将为0,因此您需要在复合中使用一些股东计数器。

实施问题:

您对访客模式的实施不完整。您没有通过复合结构的访问逻辑。

看看你打算如何使用你的访问者,我想知道你是不是想要为对象添加属性/功能,而不是浏览它们。

我建议您查看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();