什么是访问者模式?
访问者模式是一种对象行为型模式
访问者模式提供了一个作用于某对象结构中各元素操作的表现形式,它可以在不改变各元素类的前提下定义作用于这些元素的新操作。
听起来好像听不懂,我们还是举例说明。我么大学时应该经常去图书馆吧,图书馆不仅提供看书服务,还提供借书和还书的服务,对于图书馆来说,借书和还书都是一直存在的,而我们作为个人,每次去图书馆的意图却是不同的,这就是我对访问者模式的通俗理解。
核心结构
- Vistor。抽象访问者,为对象结构中具体元素声明一个访问操作,这个访问操作根据操作名称或参数类型可以清楚的知道要访问元素的具体类型。
- ConcreteVistor。具体访问者,实现了抽象访问者声明的操作,每个操作可以访问对象结构中的一种类型元素。
- Element。抽象元素,其中会定义一个接收方法,并以一个抽象访问者作为形参。
- ConcreteElement。具体元素,具体实现了接收方法,在接收方法中调用访问者的方法以便完成对一个元素的操作。
- ObjectStructure。结构对象,用于存储、枚举元素,还可以允许访问者访问它的元素。
类图展示
代码分析
模式介绍中,我们讲到了图书馆的例子,现在我们就以设计模式实现一下。
首先我们定义换来借去的书——抽象元素:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——抽象元素
*/
public abstract class Book {
protected Integer bookNo;
protected String bookName;
protected Date date;
public Book(Integer bookNo, String bookName, Date date) {
this.bookNo = bookNo;
this.bookName = bookName;
this.date = date;
}
public Integer getBookNo() {
return bookNo;
}
public void setBookNo(Integer bookNo) {
this.bookNo = bookNo;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public abstract void accept(Vistor vistor);
}
对于书的具体操作其实就是具体元素,借书:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——具体元素
*/
public class Lending extends Book {
public Lending(Integer bookNo, String bookName, Date date) {
super(bookNo, bookName, date);
}
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
}
还书:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——具体元素
*/
public class Returning extends Book {
public Returning(Integer bookNo, String bookName, Date date) {
super(bookNo, bookName, date);
}
@Override
public void accept(Vistor vistor) {
vistor.visit(this);
}
}
接下来就是声明我们,抽象访问者:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——抽象访问者
*/
public abstract class Vistor {
public abstract void visit(Lending lending);
public abstract void visit(Returning returning);
}
我们实现一个我们:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——具体访问者
*/
public class PrivateAdministrator extends Vistor {
@Override
public void visit(Lending lending) {
System.out.println("The book " + lending.getBookName() + " will be lent, book's number is " + lending.getBookNo() + " and borrowing date is " + lending.getDate());
System.out.println("Borrowing period is two weeks");
}
@Override
public void visit(Returning returning) {
System.out.println("The book " + returning.getBookName() + " will be returned, book's number is " + returning.getBookNo() + " and returning date is " + returning.getDate());
System.out.println("Must pay the rent");
}
}
我们玩一个大点的,因为图书管理员也是人,也会借书还书:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/31.
* 访问者模式——具体访问者
*/
public class LibraryAdministrator extends Vistor {
@Override
public void visit(Lending lending) {
System.out.println("The book " + lending.getBookName() + " will be lent, book's number is " + lending.getBookNo() + " and borrowing date is " + lending.getDate());
System.out.println("Borrowing period is one month");
}
@Override
public void visit(Returning returning) {
System.out.println("The book " + returning.getBookName() + " will be returned, book's number is " + returning.getBookNo() + " and returning date is " + returning.getDate());
System.out.println("No need to pay");
}
}
完活,惯例,测试代码:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/3/19.
* 访问者模式测试
*/
public class VistorTest {
public static void main(String[] args) {
vistorTest();
}
private static void vistorTest() {
try {
Vistor administrator = new LibraryAdministrator();
Vistor reader = new PrivateAdministrator();
Book lending = new Lending(001, "《Head First设计模式》", new Date());
Book returning = new Returning(001, "《Head First设计模式》", new Date());
lending.accept(administrator);
returning.accept(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
}
模式总结
优点
- 新增访问操作只需增加一个新的具体访问者,无需
- 外部调用可以在不改变现在有元素层次结构的情况下,定义作用于改层次结构的操作。
- 对元素的访问行为全部集中到访问者对象中,类的职责更加明确清晰,有利于对象结构中元素对象的复用,相同的对象结构可以提供多个不同的访问者进行访问。
缺点
- 新增一个元素需要在抽象访问者中新增一个抽象访问,违背了开闭原则。
- 元素需要暴露一个外部调用给访问者,在一定程度上违背了迪米特原则。
适用场景
- 一个对象结构包含很多元素,它们有不同的接口,而外部调用需要对这些对象进行一些访问操作。
- 需要对一个对象结构中的元素进行很多不同的、不相关的操作,访问者模式可以在一个类中把这些操作集中起来。
- 当对象结构共享时,访问者模式可以让每个外部调用仅包含需要用到的操作。
- 元素改变较少。