代码之家  ›  专栏  ›  技术社区  ›  zar

与访问者的复合模式,访问者中的真正内容是什么?

  •  0
  • zar  · 技术社区  · 8 年前

    复合模式通常用于访问者。我正试图弄清楚什么是合成的,什么是访问者的。例如,如果其中一个组合具有唯一的属性/属性,那么它是否仍然存储在其中,访问者只会将其挖掘出来,还是访问者会保留它?

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

    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有一个不适用于其他员工的territory属性,复合是否仍然存储它,访问者只检索它的值?在这种情况下,我们需要两名访客来 setValue() getValue() 如果我需要这些手术?第二个范例是访问者是否应该存储该属性,本质上添加该属性,否则该属性将不存在于员工中?

    股票期权属性如何,假设只给5%的员工。这应该以复合形式存储并由访问者扩展吗?

    我担心的一个问题是,员工复合可以有许多其他属性,这也可能意味着派生类中的差异会扩大。如果复合具有相对较大的属性,但实际上并不适用于所有子类,这可以吗?我们应该通过访问者添加属性,还是只使用访问者为一个唯一子类可能具有的特定附加属性提供接口?

    2 回复  |  直到 8 年前
        1
  •  2
  •   Christophe    8 年前

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

    • 这个 composite 是一种结构模式。其目的是表示层次结构,统一处理各个对象和对象的组合。

    • 这个 visitor 是一种行为模式。其目的是表示要在目标结构的元素上执行的操作。

    根据这一原则,访问者不在那里存储持久数据。因此,是的,任何可能特定于某类员工的属性都应保留在组合中。

    事实上,你有大量的属性(无论是特定于某个员工的派生属性还是所有员工的共同属性),这不会改变原则。访问者模式旨在通过使用不同的访问成员函数(每个不同的类一个)来处理组合中的不同类型的元素。

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

    您可以在访问者中放入一些值,只是暂时的,以执行操作。例如:

    • 你可以有一个访客来计算经理以下的人数和他们的总工资
    • 或者你可以有一个访问者的共享总数,这促使一个算法将这些共享按层次分布在员工身上。在分发结束时,访问者中剩余的股份将为0,因此您需要在组合中添加一些股东计数器。

    实施问题:

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

    看看您打算使用访问者的方式,我想知道您是否不想为对象添加属性/函数,而不是浏览它们。

    我建议你看看 decorator pattern ,更适合这种用法:它是一种结构模式,扩展了现有的类家族。

        2
  •  2
  •   Borys Serebrov    8 年前

    问题是从 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?
        }
    
    };
    

    方式 来访者 + 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 对象将访问许多对象。因此,无法“捕获”从 参观 方法

    同样,如果将单个结果保存在属性中,如在 GetTerritoryVisitor ,它将无法正常工作,因为它将被每个 SalesPerson 您访问。

    通常 来访者 处理对象或积累一些信息。

    因此,我可以想象访客创建了销售人员的区域列表:

    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();