访问者模式:

  访问者模式的官方定义是这样的:

  • 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
  • 官方的东西总是晦涩难懂的,那么我们现在就来拆解一下:
    • 首先"一个作用于某对象结构中的各元素的操作",提到了三个东西:对象结构、元素、操作。
    • 我们都学习过数据结构,数据结构中大家对数据的访问一般都是直接访问其地址。
    • 在面向对象的设计中,我们一般也是将数据的访问操作放在类的内部,便于访问。
    • 这种设计看似没有什么问题,但当我们想要采用不同方式访问数据或对象结构时就必须要对类进行修改,这样就违反了OCP原则。于是大家会想到将数据结构与操作分离开来,当问们需要添加访问操作的时候直接添加新的类,原来的代码不需要做任何改变,这也是后半句提到的"可以在不改变各元素类的前提下定义作用于这些元素的新操作"。

访问者模式的角色:

  1. Visitor:接口或抽象类,定义了对每个Element访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改visitor接口,如果出现这种情况,则说明不适合使用该模式。
  2. ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
  3. Element:元素接口或抽象类,它定义了一个接受访问者的方法(accept),其意思就是说每一个元素都可以被访问者访问。
  4. ConcreteElement:具体的元素类,它提供接受访问的具体实现,而这个具体实现通常情况下是使用访问者提供的访问该元素类的方法。
  5. ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。

优点:

  1. 各角色职责分离,符合单一职责原则。
  2. 具有优秀的扩展性,使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。
  3. 灵活性

缺点:

  1. 具体元素对访问者公布细节,违反了迪米特原则。
  2. 具体元素变更比较困难。
  3. 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

访问者模式的使用场景:

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。 

eg:

class Visitor
{
public:
    virtual void Visit( Element *element ){};
};

class Element
{
public:
    // Methods
    virtual void Accept( Visitor *visitor ){};
};

//-------------------------------------------------------
class Employee : public Element
{
public:
    string name;
    double income;
    int vacationDays;

public :
    Employee( string name, double income,
        int vacationDays )
    {
        this->name = name;
        this->income = income;
        this->vacationDays = vacationDays;
    }

    void Accept( Visitor *visitor )
    {
        visitor->Visit( this );
    }
};

class IncomeVisitor : public Visitor
{
public:    
    void Visit( Element *element )
    {
        Employee *employee = ((Employee*)element);
        employee->income *= 1.10;
        cout<<employee->name<<" 's new income: " <<employee->income<<endl;

    }
};

class VacationVisitor : public Visitor
{
public :
    void Visit( Element *element )
    {
        Employee *employee = ((Employee*)element);
        // Provide 3 extra vacation days
        employee->vacationDays += 3;        
        cout<<employee->name<<" 's new vacation days: " <<employee->income<<endl;
    }
};

class Employees
{    
private :
    list< Employee*> employees;

public :

    void Attach( Employee *employee )
    {        
        employees.push_back(employee);        
    }

    void Detach( Employee *employee )
    {
        employees.remove(employee);        
    }

    void Accept( Visitor *visitor )
    {        
        for (std::list<Employee*>::iterator it=employees.begin(); it != employees.end(); ++it)
            (*it)->Accept(visitor);
    }
};
//-------------------------------------------------------
//use
    Employees *e = new Employees();
    e->Attach( new Employee( "Tom", 25000.0, 14 ) );
    e->Attach( new Employee( "Thomas", 35000.0, 16 ) );
    e->Attach( new Employee( "Roy", 45000.0, 21 ) );

    // Create two visitors
    IncomeVisitor *v1 = new IncomeVisitor();
    VacationVisitor *v2 = new VacationVisitor();

    // Employees are visited
    e->Accept( v1 );
    e->Accept( v2 );