1、概念
访问者模式涉及两个角色,一个是访问者,另一个是被访问的资源。
访问者有多个具体的实现,资源也有多个具体的实现。
访问者与资源可以有多种组合,假如有三种类型的访问者与三种类型的资源,则它们的组合方式就有3 * 3 = 9种。
访问者解决的问题就是每种组合都能产生不同的行为。
2、示例
访问者模式解决的问题在日常工作中很常见,比如基于角色的访问权限控制。假如有三种类型的文档:普通、机密、绝密,每个文档实现都维护一个集合,里边记录着被权限访问的用户名及其密码。同时,有三种类型的访问者:匿名、实名、授权。匿名访问者只能够访问普通类型的文档。实名访问者可以访问普通、机密两种类型的文档,但是在访问机密文档时,必需提供正确的用户名。授权访问者可以访问全部的三种文件,但在访问机密文档必需提供正确的用户名,在访问绝密文档时不但要提供正确的用户名,还要提供正确的密码。实现代码如下:
package com.zhangxf.visitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 文档抽象类
abstract class Document {
private String name;
private String content;
private Map<String, String> users;
public Document(String docName, String content) {
this.name = docName;
this.content = content;
users = new HashMap<String, String>();
}
public String getDocName() {
return name;
}
public String getContent() {
return content;
}
public void addUser(String userName, String userSecret) {
users.put(userName, userSecret);
}
public boolean isUserExist(String visitorName) {
return users.containsKey(visitorName);
}
public boolean isAuthorization(String visitorName, String visitorSecretor) {
if (isUserExist(visitorName) && (users.get(visitorName) == visitorSecretor)) {
return true;
} else {
return false;
}
}
// 这个方法是关键,注意它的参数类型是visitor
public abstract String visit(Visitor v);
}
// 普通文档
class CommonDocument extends Document {
public CommonDocument(String name, String content) {
super(name, content);
}
@Override
public String visit(Visitor v) {
// 注意this参数,它代表的是CommonDocment类实例
return v.read(this);
}
}
// 机密文档
class ConfidentialDocument extends Document {
public ConfidentialDocument(String name, String content) {
super(name, content);
}
@Override
public String visit(Visitor v) {
// 注意this参数,它代表的是ConfidentialDocument类实例
return v.read(this);
}
}
// 绝密文档
class TopSecretDocument extends Document {
public TopSecretDocument(String name, String content) {
super(name, content);
}
@Override
public String visit(Visitor v) {
// 注意this参数,它代表的是TopSecretDocument类实例
return v.read(this);
}
}
abstract class Visitor {
private String visitorName;
private String visitorSecret;
public Visitor(String name, String secret) {
this.visitorName = name;
this.visitorSecret = secret;
}
public String getVisitorName() {
return visitorName;
}
public String getVisitorSecret() {
return visitorSecret;
}
// 当文档类型是不同时,调用如下三个版本的read
public abstract String read(CommonDocument doc);
public abstract String read(ConfidentialDocument doc);
public abstract String read(TopSecretDocument doc);
}
// 匿名访问者
class AnonymityVisitor extends Visitor {
public AnonymityVisitor() {
super(null, null);
}
@Override
public String read(CommonDocument doc) {
System.out.println("# Anonymity visitor can read common document: " + doc.getDocName());
return doc.getContent();
}
@Override
public String read(ConfidentialDocument doc) {
System.out.println("# Anonymity visitor can not read confidential document: " + doc.getDocName());
return null;
}
@Override
public String read(TopSecretDocument doc) {
System.out.println("# Anonymity visitor can not read top secret document: " + doc.getDocName());
return null;
}
}
// 实名访问者
class RealNameVisitor extends Visitor {
public RealNameVisitor(String visitorName) {
super(visitorName, null);
}
@Override
public String read(CommonDocument doc) {
System.out.println(
"# Real name visitor [" + getVisitorName() + "] can read common document: " + doc.getDocName());
return doc.getContent();
}
@Override
public String read(ConfidentialDocument doc) {
// 访问者名字必需正确
if (doc.isUserExist(getVisitorName())) {
System.out.println("# Real name visitor [" + getVisitorName() + "] can read confidential document: "
+ doc.getDocName());
return doc.getContent();
} else {
System.out.println("# Real name visitor [" + getVisitorName() + "] can not read confidential document: "
+ doc.getDocName());
return null;
}
}
@Override
public String read(TopSecretDocument doc) {
System.out.println("# Real name visitor can not read top secret document: " + doc.getDocName());
return null;
}
}
// 授权访问者
class AuthorizationVisitor extends Visitor {
public AuthorizationVisitor(String visitorName, String visitorSecret) {
super(visitorName, visitorSecret);
}
@Override
public String read(CommonDocument doc) {
System.out.println(
"# Authorization visitor [" + getVisitorName() + "] can read common document: " + doc.getDocName());
return doc.getContent();
}
@Override
public String read(ConfidentialDocument doc) {
// 访问者名字必需正确
if (doc.isUserExist(getVisitorName())) {
System.out.println("# Authorization visitor [" + getVisitorName() + "] can read confidential document: "
+ doc.getDocName());
return doc.getContent();
} else {
System.out.println("# Authorization visitor [" + getVisitorName() + "] can not read confidential document: "
+ doc.getDocName());
return null;
}
}
@Override
public String read(TopSecretDocument doc) {
String visitorName = getVisitorName();
String visitorSecret = getVisitorSecret();
// 访问者名字与密码都要正确
if (doc.isAuthorization(visitorName, visitorSecret)) {
System.out.println("# Authorization visitor [" + getVisitorName() + "] can read confidential document: "
+ doc.getDocName());
return doc.getContent();
} else {
System.out.println("# Authorization visitor [" + getVisitorName() + "] can not read confidential document: "
+ doc.getDocName());
return null;
}
}
}
public class VisitorPattern {
public static void main(String[] args) {
// 构建文档库,为每个文档添加允许的访问者Billy
List<Document> docLib = new ArrayList<Document>();
Document doc = new CommonDocument("Common 1", "I am common docment");
doc.addUser("Billy", "xxxxxx");
docLib.add(doc);
doc = new ConfidentialDocument("Confidential 1", "I am confidential docment");
doc.addUser("Billy", "xxxxxx");
docLib.add(doc);
doc = new TopSecretDocument("TopSecret 1", "I am confidential docment");
doc.addUser("Billy", "xxxxxx");
docLib.add(doc);
// 匿名访问者访问文档库
System.out.println("========create anonymity visitor===========");
Visitor v = new AnonymityVisitor();
for(Document item : docLib) {
item.visit(v);
}
// 实名访问者访问文档库
System.out.println("========create real name visitor Billy===========");
v = new RealNameVisitor("Billy");
for(Document item : docLib) {
item.visit(v);
}
// 实名访问者访问文档库,但用户名不正确
System.out.println("========create real name visitor Alisa===========");
v = new RealNameVisitor("Alisa");
for(Document item : docLib) {
item.visit(v);
}
// 授权访问者访问文档库
System.out.println("========create authorization visitor Billy with secret xxxxxx===========");
v = new AuthorizationVisitor("Billy", "xxxxxx");
for(Document item : docLib) {
item.visit(v);
}
// 授权访问者访问文档库,用户名正确但密码不正确
System.out.println("========create authorization visitor Billy with secret yyyyyy===========");
v = new AuthorizationVisitor("Billy", "yyyyyy");
for(Document item : docLib) {
item.visit(v);
}
}
}
运行结果:
========create anonymity visitor===========
# Anonymity visitor can read common document: Common 1
# Anonymity visitor can not read confidential document: Confidential 1
# Anonymity visitor can not read top secret document: TopSecret 1
========create real name visitor Billy===========
# Real name visitor [Billy] can read common document: Common 1
# Real name visitor [Billy] can read confidential document: Confidential 1
# Real name visitor can not read top secret document: TopSecret 1
========create real name visitor Alisa===========
# Real name visitor [Alisa] can read common document: Common 1
# Real name visitor [Alisa] can not read confidential document: Confidential 1
# Real name visitor can not read top secret document: TopSecret 1
========create authorization visitor Billy with secret xxxxxx===========
# Authorization visitor [Billy] can read common document: Common 1
# Authorization visitor [Billy] can read confidential document: Confidential 1
# Authorization visitor [Billy] can read confidential document: TopSecret 1
========create authorization visitor Billy with secret yyyyyy===========
# Authorization visitor [Billy] can read common document: Common 1
# Authorization visitor [Billy] can read confidential document: Confidential 1
# Authorization visitor [Billy] can not read confidential document: TopSecret 1
3、总结
访问者模式的实现有三个关键点:一个是抽象资源的visit方法的参数类型是访问者的抽象类。第二个是在具体资源实例的visit方法中对this指针的使用。第三个就是访问者抽象类中用多态实现的针对每种资源类型的多个版本的read。
访问者模式将资源的表示与资源的访问控制进行解耦,使代码更加容易维护也更容易扩展。