访问者模式
定义
封装一些的对于某种数据结构中的各元素的操作,它可以在不改变数据的前提下定义作用于这些元素的新的操作
访问者模式(Visitor pattern)是相对简单的模式,也可以作为迭代器模式的补充
优缺点、应用场景
优点
- 符合单一职责原则。具体角色负责数据的存储,而访问者对象Visitor负责报表的展示,职责明确区分开来
- 优秀的拓展性。由于单一职责,继续增加visitor对数据操作是容易的。
- 灵活性非常高。可以对不同的角色做定制化处理。
缺点
- 具体角色对访问者公布细节。即visitor关注了具体角色的内部细节,违反了迪米特法则。
- 具体角色变更更困难。具体角色属性的变动会导致visitor中对应角色的逻辑变更。
- 违背了依赖倒置原则。visitor依赖的是具体的角色,而不是抽象,破坏了依赖倒置原则。在面向对象编程中,直接操作具体角色会造成拓展困难。
应用场景
- 一个对象结构包含很多类对象,它们有不同的接口,又需要对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同并且不相关的操作。
模拟场景
打印报表。还原在Van的地♂牢中的场景
非访问者模式
演员的抽象和实现
/**
* 我们都是演员 抽象类
*/
public abstract class Actors {
public final static int SLAVE = 0;
public final static int MASTER = 1;
private String name;
private int power;
private int position;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
/**
* 打印演员的信息
*/
public void report() {
String info = "姓名:" + this.name + "\t";
info += "身份:" + (this.position == SLAVE ? "平家boy" : "Dungeon Master") + "\t";
info += "权力:" + this.power + "\t";
info += this.getOtherInfo();
System.out.println(info);
}
/**
* 拼装其他演员的信息
*
* @return 信息
*/
protected abstract String getOtherInfo();
}
/**
* 地牢统治者
*/
public class DungeonMaster extends Actors {
/**
* 职责
*/
private String performance;
public String getPerformance() {
return performance;
}
public void setPerformance(String performance) {
this.performance = performance;
}
@Override
protected String getOtherInfo() {
return "职责:" + this.performance + "\t";
}
}
/**
* 平家Boy
*/
public class PingjiaBoy extends Actors {
/**
* 工作内容
*/
private String job;
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
protected String getOtherInfo() {
return "工作内容:" + this.job + "\t";
}
}
入口类方法
public static void mockActors() {
List<Actors> actors = new ArrayList<>();
// 马凯
PingjiaBoy makai = new PingjiaBoy();
makai.setName("马凯");
makai.setPosition(Actors.SLAVE);
makai.setPower(0);
makai.setJob("Show~ Yes Sir");
// 吾作
PingjiaBoy wuzuo = new PingjiaBoy();
wuzuo.setName("吾作");
wuzuo.setPosition(Actors.SLAVE);
wuzuo.setPower(0);
wuzuo.setJob("Show~ Yes Sir");
// 地牢统治者
DungeonMaster van = new DungeonMaster();
van.setName("Van");
van.setPosition(Actors.MASTER);
van.setPower(10000);
van.setPerformance("Take it boy");
actors.add(van);
actors.add(makai);
actors.add(wuzuo);
for (Actors actor : actors) {
actor.report();
}
}
结果
访问者模式
小结
- 三个变动。删除report()方法,增加accept()方法,删除getOtherInfo()方法
- 即Actors都可以被访问者访问
UML图
演员的抽象和实现
/**
* 我们都是演员 抽象类
*/
public abstract class Actors {
public final static int SLAVE = 0;
public final static int MASTER = 1;
private String name;
private int power;
private int position;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
/**
* 拼装其他演员的信息
*/
public abstract void accept(IVisitor visitor);
}
/**
* 地牢统治者
*/
public class DungeonMaster extends Actors {
/**
* 职责
*/
private String performance;
public String getPerformance() {
return performance;
}
public void setPerformance(String performance) {
this.performance = performance;
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
/**
* 平家Boy
*/
public class PingjiaBoy extends Actors {
/**
* 工作内容
*/
private String job;
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
访问者抽象和实现
/**
* 观众老爷 访问者接口
*/
public interface IVisitor {
/**
* 定义可以访问的平家boy
*/
void visit(PingjiaBoy pingjiaBoy);
/**
* 定义可以访问的master
*/
void visit(DungeonMaster master);
}
/**
* 访问者实现
*/
public class Visitor implements IVisitor {
@Override
public void visit(PingjiaBoy pingjiaBoy) {
System.out.println(getPingjiaBoyInfo(pingjiaBoy));
}
@Override
public void visit(DungeonMaster master) {
System.out.println(getDungeonMasterInfo(master));
}
/**
* 组装演员的基本信息
*
* @param actor 演员对象
* @return 演员对象的基本信息
*/
private String getBaseInfo(Actors actor) {
String info = "姓名:" + actor.getName() + "\t";
info += "身份:" + (actor.getPosition() == Actors.SLAVE ? "平家boy" : "Dungeon Master") + "\t";
info += "权力:" + actor.getPower() + "\t";
return info;
}
/**
* 组装master的信息
*
* @param master master对象
* @return master的信息
*/
private String getDungeonMasterInfo(DungeonMaster master) {
String info = this.getBaseInfo(master);
String masterInfo = "职责:" + master.getPerformance() + "\t";
return info + masterInfo;
}
/**
* 组装平家boy的信息
*
* @param pingjiaBoy 平家boy对象
* @return 平家boy的信息
*/
private String getPingjiaBoyInfo(PingjiaBoy pingjiaBoy) {
String info = this.getBaseInfo(pingjiaBoy);
String pingjiaBoyInfo = "工作内容:" + pingjiaBoy.getJob() + "\t";
return info + pingjiaBoyInfo;
}
}
入口类方法
public static void visitor() {
List<Actors> actors = new ArrayList<>();
// 马凯
PingjiaBoy makai = new PingjiaBoy();
makai.setName("马凯");
makai.setPosition(Actors.SLAVE);
makai.setPower(0);
makai.setJob("Show~ Yes Sir");
// 吾作
PingjiaBoy wuzuo = new PingjiaBoy();
wuzuo.setName("吾作");
wuzuo.setPosition(Actors.SLAVE);
wuzuo.setPower(0);
wuzuo.setJob("Show~ Yes Sir");
// 地牢统治者
DungeonMaster van = new DungeonMaster();
van.setName("Van");
van.setPosition(Actors.MASTER);
van.setPower(10000);
van.setPerformance("Take it boy");
actors.add(van);
actors.add(makai);
actors.add(wuzuo);
IVisitor visitor = new Visitor();
for (Actors actor : actors) {
actor.accept(visitor);
}
}
结果
参考书籍
秦小波《设计模式之禅》