访问者模式(Visitor)
1、概述
①定义
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必备条件。
——GoF《Design Pattern》
- “对象结构”:是一个具体类,封装了若干元素。
- “不改变各元素类”:不对“对象结构”中的元素进行修改。
- “新操作”:对“对象结构”中的元素进行的非侵入式操作,可定义若干个。
②结构
UML图:
- Element为元素接口,定义了一个抽象方法accept(Visitor visitor)
- ConcreteElementA、ConcreteElementB为Element接口的实现类,实现了accept(Visitor visitor)方法,并定义了属性data,用于存储ConcreteElementA、ConcreteElementB元素内的数据。
- Visitor为访问者接口,定义了visit(Element e)抽象方法
- ConcreteVisitorA、ConcreteVisitorB实现了Visitor接口,实现了visit(Element e)方法。
- ObjectStructure 结构对象角色,这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
③应用场景
- 一个对象结构包含很多类对象(元素对象),它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的操作,而你想避免让这些操作修改这些对象的类。Visitor模式使得你可以将相关的操作集中起来定义在一个类中。
- 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类(ObjectStructure)很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对应所有访问者的接口方法,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好,不适合使用访问者模式了。
2、实例分析
UML图:
代码:
import java.util.Vector;
/**
* @author Hanlin Wang
*/
public class VisitorMode {
public static void main(String[] args) {
//准备数据
TravelAgency agency = new TravelAgency();
agency.addSpot(new GreatWall());
agency.addSpot(new Disneyland());
//游客A产生
VisitorA visitorA = new VisitorA();
agency.guideToGreatWall(visitorA);
agency.guideToDisneyland(visitorA);
System.out.println("
");
//游客B产生
VisitorB visitorB = new VisitorB();
agency.guideToGreatWall(visitorB);
agency.guideToDisneyland(visitorB);
//运行结果
/*
* 游客:VisitorA,以猥琐的方式~游览了 : GreatWall, 获取的景区的data为 : 但使龙城飞将在,不教胡马度阴山。
游客:VisitorA,以猥琐的方式~游览了 : Disneyland, 获取的景区的data为 : 上海迪斯尼乐园,童话般的世界。
游客:VisitorB,以拉风的方式~游览了 : GreatWall, 因为太帅,获得该景区的data为 : 但使龙城飞将在,不教胡马度阴山。
游客:VisitorB,以拉风的方式~访问了 : Disneyland, 因为太帅,获得该景区的data为 : 上海迪斯尼乐园,童话般的世界。
*/
}
}
interface Visitor{
void visitGreatWall(GreatWall spot);
void visitDisneyland(Disneyland spot);
}
interface ScenicSpots{
void accept(Visitor visitor);
}
class TravelAgency{
private Vector<ScenicSpots> scenicSpots = new Vector<ScenicSpots>();
public void addSpot(ScenicSpots spot){
scenicSpots.add(spot);
}
public void removeSpot(ScenicSpots spot){
scenicSpots.remove(spot);
}
public void guideToGreatWall(Visitor visitor){
scenicSpots.get(0).accept(visitor);
}
public void guideToDisneyland(Visitor visitor){
scenicSpots.get(1).accept(visitor);
}
}
class GreatWall implements ScenicSpots{
private String data = "但使龙城飞将在,不教胡马度阴山。";
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public void accept(Visitor visitor) {
visitor.visitGreatWall(this);
}
}
class Disneyland implements ScenicSpots{
private String data = "上海迪斯尼乐园,童话般的世界。";
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public void accept(Visitor visitor) {
visitor.visitDisneyland(this);
}
}
class VisitorA implements Visitor{
public void visitGreatWall(GreatWall spot) {
System.out.println("游客:"+this.getClass().getSimpleName()+",以猥琐的方式~"
+ "游览了 : "+spot.getClass().getSimpleName()+", 获取的景区的data为 : "+ spot.getData());
}
public void visitDisneyland(Disneyland spot) {
System.out.println("游客:"+this.getClass().getSimpleName()+",以猥琐的方式~"
+ "游览了 : "+spot.getClass().getSimpleName()+", 获取的景区的data为 : "+ spot.getData());
}
}
class VisitorB implements Visitor{
public void visitGreatWall(GreatWall spot) {
System.out.println("游客:"+this.getClass().getSimpleName()+",以拉风的方式~"
+ "游览了 : "+spot.getClass().getSimpleName()+", 因为太帅,获得该景区的data为 : "+ spot.getData());
}
public void visitDisneyland(Disneyland spot) {
System.out.println("游客:"+this.getClass().getSimpleName()+",以拉风的方式~"
+ "访问了 : "+spot.getClass().getSimpleName()+", 因为太帅,获得该景区的data为 : "+ spot.getData());
}
}
分析:
- Visitor接口中定义了两个方法:VisitGreatWall(GreatWall spot)、VisitDisneyland(Disneyland spot)方法,这两个方法用于与GreatWall、Disneyland元素做数据交互,但是不会修改TravelAgency的内部数据结构。
- ScenicSpots为元素接口,定义了accept(Visitor visitor)方法,表示可以接受为Visitor接口的任意实现类作为参数与该元素做数据交互,不改变该元素的数据结构。
- VisitorA、VisitorB为实现Visitor接口VisitGreatWall(GreatWall spot)、VisitDisneyland(Disneyland spot)方法的具体类,也就是访问者模式中的访问者类。我们可以把VisitorA与VisitorB看作封装了两个不同算法的访问者类,与ScenicSpots元素接口的实现类作非侵入式交互(不改变元素类的数据结构)。
- GreatWall、Disneyland为实现ScenicSpots接口的具体类。GreatWall、Disneyland为元素类,通过accept(Visitor visitor)被Visitor类用来做数据交互。
- TravelAgency为结构体类,相当于代理模式中的ObjectStructure。TravelAgency封装了一个ScenicSpots类型的集合,相当于封装了元素类,并使用guideToGreatWall(Visitor visitor)、guideToDisneyland(Visitor visitor)方法将访问者对象引入对于元素的accept方法中(guideToGreatWall将访问者对象引入GreatWall元素中的accept方法中,guideToDisneyland也一样),使得访问者类在不改变TravelAgency类的数据结构(也就是TravelAgency类中的元素scenicSpots)的前提下与scenicSpots集合中的元素做数据交互。我们可以看到,data就是数据,访问者取出了data的数据的值,这样就达到了访问者模式的目的。
3、总结
呼~设计模式系列已全部上线,大家可以逐一查阅。希望大家有帮助!我相信大家对某些比较难的设计模式还是有疑惑的,欢迎大家给我留言,也可以加我QQ:346319800与我交流互动,我一定会给大家解答疑惑。有些话在文章中是写不出来的~
晚安~