访问者模式
访问者模式-封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。(Visitor Pattern),行为型。
访问者模式-是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
模式和结构定义
- Visitor是抽象访问者,为该对象结构中ConcreteElement的每一个类声明一个Visit操作;
- ConcreteVisitor是具体访问者,实现每个由visitor声明的操作,是每个操作实现算法的一部分,而该算法片段是对应于结构中对象的类;
- ObjectStructure为能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素;
- Element定义了一个Accept操作,它以一个访问者为参数;
- ConcreteElement为具体元素,实现Accept操作。
应用实例
下面举一个例子
你和朋友的女朋友同时来朋友家拜访。朋友家有两个厕所,一个在客厅,一个在主卧。
你是访问者,朋友接收你的访问,朋友对你做一些条件判断。你可以访问或者使用他家的某些东西,你可以使用客厅的厕所,不能使用主卧内的厕所。
朋友的女朋友也是访问者,朋友接收她的访问。朋友对她做一些条件判断。朋友的女朋友可以使用客厅的厕所,还可以使用主卧内的厕所。
这就是访问者模式。
家庭设备类
/**
* 家庭设备
*
* @author shengyong.huang
* @date 2020-07-25
*/
public abstract class AbstractHouse {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void accept(IVisitor visitor);
}
家庭设备-客厅厕所
/**
* 客厅厕所
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class SittingRoomToilet extends AbstractHouse {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
家庭设备-主卧厕所
/**
* 主卧厕所
* @author shengyong.huang
* @date 2020-07-25
*/
public class MasterBedroomToilet extends AbstractHouse {
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
访问者接口
/**
* 访问者接口
*
* @author shengyong.huang
* @date 2020-07-25
*/
public interface IVisitor {
void visit(SittingRoomToilet sittingRoomToilet);
void visit(MasterBedroomToilet masterBedroomToilet);
}
自己访问朋友家实现
/**
* @author shengyong.huang
* @date 2020-07-25
*/
public class SelfVisitor implements IVisitor {
@Override
public void visit(SittingRoomToilet sittingRoomToilet) {
System.out.println("Self可以访问朋友的:" + sittingRoomToilet.getName());
}
@Override
public void visit(MasterBedroomToilet masterBedroomToilet) {
System.out.println("Self不能访问朋友的:" + masterBedroomToilet.getName());
}
}
朋友的女朋友访问朋友家实现
/**
* @author shengyong.huang
* @date 2020-07-25
*/
public class GirlFriendVisitor implements IVisitor {
@Override
public void visit(SittingRoomToilet sittingRoomToilet) {
System.out.println("GirlFriend可以访问朋友的:" + sittingRoomToilet.getName());
}
@Override
public void visit(MasterBedroomToilet masterBedroomToilet) {
System.out.println("GirlFriend可以访问朋友的:" + masterBedroomToilet.getName());
}
}
测试方法
public class TestMain {
public static void main(String[] args) {
List<AbstractHouse> houseList = new ArrayList<>();
SittingRoomToilet sittingRoomToilet = new SittingRoomToilet();
sittingRoomToilet.setName("客厅厕所");
MasterBedroomToilet masterBedroomToilet = new MasterBedroomToilet();
masterBedroomToilet.setName("主卧厕所");
houseList.add(sittingRoomToilet);
houseList.add(masterBedroomToilet);
for (AbstractHouse house : houseList) {
house.accept(new SelfVisitor());
}
for (AbstractHouse house : houseList) {
house.accept(new GirlFriendVisitor());
}
}
}
结果展示
Self可以访问:客厅厕所
Self不能访问:主卧厕所
GirlFriend可以访问:客厅厕所
GirlFriend可以访问:主卧厕所
优点和不足
优点
- 增加新的操作很容易,即增加一个新的访问者
缺点
- 增加新的数据结构困难
- 具体元素变更比较麻烦
- 违背了依赖倒置原则。访问者依赖的是具体元素,而不是抽象元素
使用场景
- 数据结构与数据操作分离