访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
它的UML类图如下
为便于理解,举如下示例
要在控制台输出如下效果(考虑扩展性,比如要添加结婚时等等状态应该怎么办):
Mang Success时,背后多半有个伟大的女人。
Womang Success时,背后多半有个不成功的男人。
Man Failing时,闷头喝酒,谁也不用劝。
Woman Failing时,眼泪汪汪,谁劝也没用。
Man Amativeness时,凡事不懂也要装懂。
Woman Amativeness时,遇事懂也装不懂。
访问者模式基本代码如下:
Visitor类(例子中的状态类),为该对象结构中ConcreteElement的每一个类声明一个visit操作。对应例子中的代码:
public abstract class Action {
public abstract void getManConclusion(Man concreteElementA);
public abstract void getWomanConclusion(Woman concreteElementB);
}
ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。对应例子中的代码:
public class Success extends Action {
@Override
public void getManConclusion(Man concreteElementA) {
System.out.println(concreteElementA.getClass().getName()+this.getClass().getName()+"时,背后多半有个伟大的女人。");
}
@Override
public void getWomanConclusion(Woman concreteElementB) {
System.out.println(concreteElementB.getClass().getName()+this.getClass().getName()+"时,背后多半有个不成功的男人。");
}
}
public class Failing extends Action {
@Override
public void getManConclusion(Man concreteElementA) {
System.out.println(concreteElementA.getClass().getName()+this.getClass().getName()+"时,闷头喝酒,谁也不用劝。");
}
@Override
public void getWomanConclusion(Woman concreteElementB) {
System.out.println(concreteElementB.getClass().getName()+this.getClass().getName()+"时,眼泪汪汪,谁劝也没用。");
}
}
public class Amativeness extends Action {
@Override
public void getManConclusion(Man concreteElementA) {
System.out.println(concreteElementA.getClass().getName()+this.getClass().getName()+"时,凡事不懂也要装懂。");
}
@Override
public void getWomanConclusion(Woman concreteElementB) {
System.out.println(concreteElementB.getClass().getName()+this.getClass().getName()+"时,遇事懂也装不懂。");
}
}
Element类,定义一个accept操作,参数是一个访问者。对应代码:
public abstract class Person {
public abstract void accept(Action visitor);
}
ConcreteElementA和ConcreteElementB类,具体元素,实现accept操作。对应代码:
public class Man extends Person {
@Override
public void accept(Action visitor) {
visitor.getManConclusion(this);
}
}
public class Woman extends Person {
@Override
public void accept(Action visitor) {
visitor.getWomanConclusion(this);
}
}
ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。
public class ObjectStructure {
private List<Person> elements = new ArrayList<>();
public void attach(Person element) {
elements.add(element);
}
public void detach(Person element) {
elements.remove(element);
}
public void display(Action visitor) {
for(Person p : elements) {
p.accept(visitor);
}
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.attach(new Man());
os.attach(new Woman());
Success v1 = new Success();
os.display(v1);
Failing v2 = new Failing();
os.display(v2);
Amativeness v3 = new Amativeness();
os.display(v3);
}
}
当要增加结婚状态时,只需要新增加一个Marriage类:
public class Marriage extends Action {
@Override
public void getManConclusion(Man concreteElementA) {
System.out.println(concreteElementA.getClass().getName()+this.getClass().getName()+"时,感慨道:恋爱游戏结束,有妻徒刑开始。");
}
@Override
public void getWomanConclusion(Woman concreteElementB) {
System.out.println(concreteElementB.getClass().getName()+this.getClass().getName()+"时,欣慰曰:爱情长跑路漫漫,婚姻保险保平安。");
}
}
客户端代码,增加下面一段:
......
Marriage v4 = new Marriage();
os.display(v4);
......
总结:
访问者模式可以实施的前提状态类中的抽象方法是固定的。如果说人类的性别不止男和女,那新增一种类别,就要在状态类和它的子类中新增一个方法,就不符合开闭原则。
因此访问者模式适用于数据结构相对稳定的系统。它把数据结构和作用于结构上的操作之间的耦合解开,使得操作集合可以相对自由地演化。