访问者模式
表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。当你想要一个为一个对象的组合增加新的能力,且不用改变封装的结构元素时,就可以使用访问者模式。
类型:
行为型模式(通过中间类的行为型模式)
访问者模式中的几个角色:
- 抽象访问者(Visitor)角色:抽象的访问者接口,定义针对所有的不同的具体节点做出的对应的反应。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者所定义的接口
- 抽象节点(Node)角色:抽象的树节点接口,定义接受一个访问者对象作为参数。
- 具体节点(ConcreteNode)角色:实现了抽象节点所规定的接受操作,以自身作为访问者对象调用的参数。
- 结构对象(ObjectStructure)角色:保存所有具体节点的容器。
访问者模式关系图:
帮助理解的图:
访问者模式示例:
抽象访问者(Visitor)角色:
/**
* Create by zhaihongwei on 2018/4/3
*/
public interface Visitor {
void visitTreeNodeA(TreeNodeA nodeA);
void visitTreeNodeB(TreeNodeB nodeB);
}
具体访问者(ConcreteVisitor)角色:(VisitorA,VisitorB)
/**
* Create by zhaihongwei on 2018/4/3
*/
public class VisitorA implements Visitor{
@Override
public void visitTreeNodeA(TreeNodeA nodeA) {
System.out.println("这是树节点A对于访问者A,所做的事");
}
@Override
public void visitTreeNodeB(TreeNodeB nodeB) {
System.out.println("这是树节点B对于访问者A,所做的事");
}
}
/**
* Create by zhaihongwei on 2018/4/3
*/
public class VisitorB implements Visitor {
@Override
public void visitTreeNodeA(TreeNodeA nodeA) {
System.out.println("这是树节点A对于访问者B,所做的事");
}
@Override
public void visitTreeNodeB(TreeNodeB nodeB) {
System.out.println("这是树节点B对于访问者B,所做的事");
}
}
抽象节点(Node)角色:
/**
* Create by zhaihongwei on 2018/4/3
*/
public interface TreeNode {
void accept(Visitor visitor);
}
具体节点(ConcreteNode)角色:(TreeNodeA,TreeNodeB)
/**
* Create by zhaihongwei on 2018/4/3
*/
public class TreeNodeA implements TreeNode{
@Override
// 一次分派
public void accept(Visitor visitor) {
// 二次分派
visitor.visitTreeNodeA(this);
}
}
/**
* Create by zhaihongwei on 2018/4/3
*/
public class TreeNodeB implements TreeNode{
@Override
// 一次分派
public void accept(Visitor visitor) {
// 二次分派
visitor.visitTreeNodeB(this);
}
}
结构对象(ObjectStructure)角色:
/**
* Create by zhaihongwei on 2018/4/3
* 保存所有同类树节点的容器
*/
public class ObjectStructure {
private List<TreeNode> treeNodes = new ArrayList<>();
/**
* 添加新的树节点
* @param treeNode
*/
public void add(TreeNode treeNode) {
treeNodes.add(treeNode);
}
/**
* 删除指定的树节点
* @param treeNode
*/
public void remove(TreeNode treeNode) {
treeNodes.remove(treeNode);
}
/**
* 遍历不同节点的,所做的不同的事
* @param visitor
*/
public void doSomething(Visitor visitor) {
for(TreeNode treeNode : treeNodes) {
treeNode.accept(visitor);
}
}
}
测试类:
/**
* Create by zhaihongwei on 2018/4/3
* 测试类
*/
public class VisitorTest {
public static void main(String[] args) {
// 创建容器对象,用来添加新的节点
ObjectStructure structure = new ObjectStructure();
// 创建A,B两种树节点,可以添加多个,但是多个就失去了访问者模式的优势。
TreeNode nodeA = new TreeNodeA();
TreeNode nodeB = new TreeNodeB();
// 向容器中添加A,B节点
structure.add(nodeA);
structure.add(nodeB);
// 创建访问者A对象
Visitor visitorA = new VisitorA();
// A,B两种节点对象访问者A做出的反应
structure.doSomething(visitorA);
System.out.println("----------------------------------");
// 创建访问者A对象
Visitor visitorB = new VisitorB();
// A,B两种节点对象访问者B做出的反应
structure.doSomething(visitorB);
}
}
测试结果:
这是树节点A对于访问者A,所做的事
这是树节点B对于访问者A,所做的事
----------------------------------
这是树节点A对于访问者B,所做的事
这是树节点B对于访问者B,所做的事
总结:
通过访问者模式可以在不改变具体TreeNode节点的情况下,为不同的节点添加同一种状态,并且每个具体节点对这个状态做出的反应是不同的。如果节点过多或者经常变化,所有的访问这对象都要跟着变化,这样就是去了访问者模式的优势。这是我觉得最难理解的一种设计模式,所以下面列出了大话设计模式中的访问者模式的例子,帮助理解。
大话设计模式的例子:
抽象访问者(Visitor)角色:
/**
* Create by zhaihongwei on 2018/4/3
*/
public interface Visitor {
/**
* 获得男人的状态
* @param man
*/
void getManState(Man man);
/**
* 获得女人的状态
* @param woman
*/
void getWomanState(Woman woman);
}
具体访问者(ConcreteVisitor)角色:成功时
/**
* Create by zhaihongwei on 2018/4/3
*/
public class SuccessVisitor implements Visitor {
@Override
public void getManState(Man man) {
System.out.println("男人成功时,背后多半有一个伟大的女人");
}
@Override
public void getWomanState(Woman woman) {
System.out.println("女人成功时,背后多半有一个失败的男人");
}
}
抽象节点(Node)角色:
/**
* Create by zhaihongwei on 2018/4/3
*
*/
public interface Human {
/**
* 接受状态对象
* @param visitor
*/
void accept(Visitor visitor);
}
具体节点(ConcreteNode)角色:(Man,Woman)
/**
* Create by zhaihongwei on 2018/4/3
* 具体节点类
*/
public class Man implements Human{
@Override
public void accept(Visitor visitor) {
visitor.getManState(this);
}
}
/**
* Create by zhaihongwei on 2018/4/3
*/
public class Woman implements Human {
@Override
public void accept(Visitor visitor) {
visitor.getWomanState(this);
}
}
结构对象(ObjectStructure)角色:
/**
* Create by zhaihongwei on 2018/4/3
* 保存人类性别的容器
*/
public class ObjectStructure {
private List<Human> humanList = new ArrayList<>();
/**
* 添加人类性别
* @param human
*/
public void add(Human human) {
humanList.add(human);
}
/**
* 删除指定的人类性别
* @param human
*/
public void remove(Human human) {
humanList.remove(human);
}
/**
* 遍历所有性别的人类对于相同的访问者,所做的不同反应
* @param visitor
*/
public void doSomething(Visitor visitor) {
for(Human human : humanList) {
human.accept(visitor);
}
}
}
测试类:
/**
* Create by zhaihongwei on 2018/4/3
* 保存人类性别的容器
*/
public class ObjectStructure {
private List<Human> humanList = new ArrayList<>();
/**
* 添加人类性别
* @param human
*/
public void add(Human human) {
humanList.add(human);
}
/**
* 删除指定的人类性别
* @param human
*/
public void remove(Human human) {
humanList.remove(human);
}
/**
* 遍历所有性别的人类对于相同的访问者,所做的不同反应
* @param visitor
*/
public void doSomething(Visitor visitor) {
for(Human human : humanList) {
human.accept(visitor);
}
}
}
测试结果:
男人成功时,背后多半有一个伟大的女人
女人成功时,背后多半有一个失败的男人
访问者模式优缺点:
优点:
- 允许对所有节点加入新的操作(新的访问者对象),并且不改变节点对象本身
- 加入新的操作相对容易
缺点:
- 实现起来过于复杂(强行使用两次分派,建议只有在必须使用访问者模式的时候才需要使用访问者模式)
- 打破了节点类的封装。