博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【设计模式】 访问者模式
阅读量:4322 次
发布时间:2019-06-06

本文共 3868 字,大约阅读时间需要 12 分钟。

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 
#include
#include
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<
name<<" 's new income: " <
income<
vacationDays += 3; cout<
name<<" 's new vacation days: " <
income<
employees; public : void Attach( Employee *employee ) { employees.push_back(employee); } void Detach( Employee *employee ) { employees.remove(employee); } void Accept( Visitor *visitor ) { for (std::list
::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

你可能感兴趣的文章
iOS酷炫动画效果合集
查看>>
[CSS] Scale on Hover with Transition
查看>>
状压DP(挑战程序设计竞赛)
查看>>
POJ 2386
查看>>
腾讯云“动态加速”与“CDN”的区别——浅谈对“动态加速”的理解(可能有误)...
查看>>
Spring源码学习笔记(5)
查看>>
Objective-C 日记⑧ 对象初始化
查看>>
mybatis中#{}与${}的区别
查看>>
RTP/RTSP/RTCP的区别和应用
查看>>
Adaboost算法简介
查看>>
在【此电脑】隐藏【设备和驱动器】中不需要的图标
查看>>
【Leetcode】【Medium】Palindrome Partitioning
查看>>
51单片机 | 实现数码管动态显示
查看>>
十进制向十六进制的转换
查看>>
练习JsonJquery查找数据
查看>>
如何使用Goolge Timeline工具
查看>>
POJ3667 Hotel
查看>>
深入浅出 Java Concurrency (16): 并发容器 part 1 ConcurrentMap (1)[转]
查看>>
深入浅出 Java Concurrency (23): 并发容器 part 8 可阻塞的BlockingQueue (3)[转]
查看>>
将博客搬至CSDN
查看>>