【设计模式】 访问者模式

1、定义

1.1 标准定义

  访问者模式(Visitor Pattern)是一个相对简单的模式,其定义如下:Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.(封装一些作用于某种数据结构中的各元素的操作, 它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。)

1.2 类图

【设计模式】 访问者模式

  ● Visitor——抽象访问者
  抽象类或者接口, 声明访问者可以访问哪些元素, 具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

  ● ConcreteVisitor——具体访问者
  它影响访问者访问到一个类后该怎么干, 要做什么事情。

  ● Element——抽象元素
  接口或者抽象类, 声明接受哪一类访问者访问, 程序上是通过accept方法中的参数来定义的。

  ● ConcreteElement——具体元素
  实现accept方法, 通常是visitor.visit(this), 基本上都形成了一种模式了。

  ● ObjectStruture——结构对象
  元素产生者, 一般容纳在多个不同类、 不同接口的容器, 如ListSetMap等, 在项目中, 一般很少抽象出这个角色。

2、实现

2.1 类图

【设计模式】 访问者模式

  抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。

  具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。

  抽象节点(Element)角色:声明一个接受操作,接受一个访问者对象作为一个参量。

  具体节点(ConcreteElement)角色:实现了抽象元素所规定的接受操作。

  结构对象(ObiectStructure)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。 

2.2 代码

#include <iostream>  
#include <string>  
#include <list>  
using namespace std;  

class Element;  

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

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


// "ConcreteElement"  
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;  
    }  
};  

// "ObjectStructure"  
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);  
    }  
};  

void main( )  
{  
    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 );  
}  

3、总结

3.1 优点

  ● 符合单一职责原则
  具体元素角色也就是Employee抽象类的两个子类负责数据的加载, 而Visitor类则负责报表的展现, 两个不同的职责非常明确地分离开来, 各自演绎变化。

  ● 优秀的扩展性
  由于职责分开, 继续增加对数据的操作是非常快捷的

  ● 灵活性非常高
  如果有
EmployeeA、EmployeeB、EmployeeC三个甚至更多的元素需要处理,每个元素处理不同,如果你用迭代器加类型判断,OK!可以完成功能,但可能效率很低,多个判断显得代码很乱,如果你用访问者模式,重载某个函数,参数分别传入EmployeeA、EmployeeB、EmployeeC三个类,是不是可以一步解决呢,这就是访问者模式的优势所在,所以,在某些处理方式上面,访问者模式的灵活性相当高。

3.2 缺点

  ● 具体元素对访问者公布细节
  访问者要访问一个类就必然要求这个类公布一些方法和数据, 也就是说访问者关注了其他类的内部细节, 这是迪米特法则所不建议的。

  ● 具体元素变更比较困难
  具体元素角色的增加、 删除、 修改都是比较困难的, 就上面那个例子, 你想想, 你要是想增加一个成员变量, 如年龄ageVisitor就需要修改, 如果Visitor是一个还好办, 多个呢?业务逻辑再复杂点呢?

  ● 违背了依赖倒置转原则
  访问者依赖的是具体元素, 而不是抽象元素, 这破坏了依赖倒置原则, 特别是在面向对象的编程中, 抛弃了对接口的依赖, 而直接依赖实现类, 扩展比较难。

3.3 使用场景

  ● 一个对象结构包含很多类对象, 它们有不同的接口, 而你想对这些对象实施一些依赖于其具体类的操作, 也就说是用迭代器模式已经不能胜任的情景。

  ● 需要对一个对象结构中的对象进行很多不同并且不相关的操作, 而你想避免让这些操作污染这些对象的类。

  总结一下, 在这种地方你一定要考虑使用访问者模式: 业务规则要求遍历多个不同的对象。 这本身也是访问者模式出发点, 迭代器模式只能访问同类或同接口的数据( 当然了, 如果你使用instanceof, 那么能访问所有的数据, 这没有争论) , 而访问者模式是对迭代器模式的扩充, 可以遍历不同的对象, 然后执行不同的操作, 也就是针对访问的对象不同, 执行不同的操作。 访问者模式还有一个用途, 就是充当拦截器( Interceptor) 角色。

原文链接: https://www.cnblogs.com/ChinaHook/p/7302499.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    【设计模式】 访问者模式

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/399516

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月13日 上午9:24
下一篇 2023年4月13日 上午9:25

相关推荐