概述
- 定义 : 封装作用于某数据结构(如List/Set/Map等)中的各元素的操作
- 可以在不改变各元素的类的前提下, 定义左用于这些元素的操作
- 类型 : 行为型
适用场景
- 一个数据结构(如List/Set/Map等)包含很多类型对象
- 数据结构与数据操作分离
优点
- 增加新的操作很容易, 即增加一个新的访问者
缺点
- 增加新的数据结构困难
- 具体元素变更比较麻烦
模式角色
-
Visitor(访问者) : 为该对象结构中C o n c r e t e E l e m e n t的每一个类声明一个 Vi s i t操作。该操作的名字和特征标识了发送Vi s i t请求给该访问者的那个类。这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
-
ConcreteVisitor(具体访问者) : 实现每个由Vi s i t o r声明的操作。每个操作实现本算法的一部分,而该算法片断乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
-
Element(元素) : 定义一个Accept操作,它以一个访问者为参数。
-
ConcreteElement(具体元素) : 实现Accept操作,该操作以一个访问者为参数。
-
ObjectStructure(对象结构) :
- 能枚举它的元素
- 可以提供一个高层的接口以允许该访问者访问它的元素
- 可以是一个复合或是一个集合,如一个列表或一个无序集合
代码实现
场景
一个观察者, 访问两个Element,ConcreteElementA 和 ConcreteElementB, ConcreteElementA 只有name属性, 所以访问它是只展示name值, ConcreteElementB除了name之外, 还具有一个绰号nickname属性, 所以访问它要展示两个属性, 类命名方式和角色名保持一致
UML类图
代码
/**
* Element(元素)角色
* @author 七夜雪
* @create 2018-11-24 21:36
*/
public interface Element {
public void accept(Visitor visitor);
}
/**
* ConcreteElement(具体元素)
* @author 七夜雪
* @create 2018-11-24 21:36
*/
public class ConcreteElementA implements Element {
private String name;
public ConcreteElementA(String name) {
this.name = name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
}
/**
* ConcreteElement(具体元素)
* @author 七夜雪
* @create 2018-11-24 21:37
*/
public class ConcreteElementB implements Element {
private String name;
private String nickname;
public ConcreteElementB(String name, String nickname) {
this.name = name;
this.nickname = nickname;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
public String getNickname() {
return nickname;
}
}
/**
* 访问者接口
* Visitor(访问者)角色
* @author 七夜雪
* @create 2018-11-24 21:29
*/
public interface Visitor {
public void visit(ConcreteElementA element);
public void visit(ConcreteElementB element);
}
/**
* ConcreteVisitor(具体访问者) 角色
* @author 七夜雪
* @create 2018-11-24 21:37
*/
public class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("访问者姓名 : " + element.getName());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("访问者姓名 : " + element.getName() + ", 江湖人称 : " + element.getNickname());
}
}
测试代码 :
/**
* 测试类
*
* @author 七夜雪
* @create 2018-11-24 21:48
*/
public class Client {
public static void main(String[] args) {
// 这个List对应ObjectStructure(对象结构) 角色
List<Element> elements = new ArrayList<>();
Element elementA = new ConcreteElementA("七夜雪");
Element elementB = new ConcreteElementB("杨过", "神雕大侠");
elements.add(elementA);
elements.add(elementB);
for (Element element : elements) {
element.accept(new ConcreteVisitor());
}
}
}
本文参考:
慕课网<java设计模式精讲 Debug 方式+内存分析>课程
四人帮<设计模式>