书上的原句:
访问者模式:表示一个作用在某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作
一个例子:我们先用最简单的多态来实现一个动物的行为:
public class Main {
public static void main(String[] args) {
Animal bird = new Bird();
bird.setState("flying");
bird.action();
bird.setState("eating");
bird.action();
Animal bat = new Bat();
bat.setState("eating");
bat.action();
bat.setState("flying");
bat.action();
}
}
abstract class Animal {
protected String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
abstract void action();
}
class Bird extends Animal {
@Override
void action() {
if ("flying".equals(state)){
System.out.println("Bird flying");
}
else if ("eating".equals(state)){
System.out.println("Bird eating");
}
}
}
class Bat extends Animal {
@Override
void action() {
if ("flying".equals(state)){
System.out.println("Bat flying");
}
else if ("eating".equals(state)){
System.out.println("Bat eating");
}
}
}
运行结果:
Bird flying
Bird eating
Bat eating
Bat flying
如果需要为动物类添加一个playing的动作,就必须改动 Animal、Bird、Bat三个类,不符合开闭原则,可以用访问者模式来改进:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Bird());
objectStructure.attach(new Bat());
objectStructure.display(new FlyAction());
objectStructure.display(new EatAction());
objectStructure.display(new PlayAction());
}
}
//对象结构
class ObjectStructure {
List<Animal> list = new ArrayList<>();
//添加动物
public void attach(Animal animal){
list.add(animal);
}
//删除动物
public void detach(Animal animal){
list.remove(animal);
}
//依次用action访问各个对象
public void display(Action action){
for (Animal animal : list){
animal.accept(action);
}
}
}
//抽象visitor访问者
abstract class Action {
abstract void doBirdAction(Bird bird);
abstract void doBatAction(Bat bat);
}
class FlyAction extends Action {
@Override
void doBirdAction(Bird bird) {
System.out.println(bird.getClass().getSimpleName() + " flying");
}
@Override
void doBatAction(Bat bat) {
System.out.println(bat.getClass().getSimpleName() + " flying");
}
}
class EatAction extends Action {
@Override
void doBirdAction(Bird bird) {
System.out.println(bird.getClass().getSimpleName() + " eating");
}
@Override
void doBatAction(Bat bat) {
System.out.println(bat.getClass().getSimpleName() + " eating");
}
}
//这时只需要再添加一个PlayAction就可以了,完全符合开闭原则
class PlayAction extends Action {
@Override
void doBirdAction(Bird bird) {
System.out.println(bird.getClass().getSimpleName() + " playing");
}
@Override
void doBatAction(Bat bat) {
System.out.println(bat.getClass().getSimpleName() + " playing");
}
}
abstract class Animal {
//让动物接受一个行为
abstract void accept(Action action);
}
class Bird extends Animal {
@Override
void accept(Action action) {
action.doBirdAction(this);
}
}
class Bat extends Animal {
@Override
void accept(Action action) {
action.doBatAction(this);
}
}
运行结果:
Bird flying
Bat flying
Bird eating
Bat eating
Bird playing
Bat playing
但访问者模式也有局限性:设计之初被访问类的数量必须是确定的,例如上述的Bird和Bat,如果突然要另外加入一个Dragon类,那么各个Action类就要相应地加上一个doDragonAction的方法了,这种情况下也不符合开闭原则,而且Action类比较多的情况下,修改会更加的繁杂。