以不同地方的厨师对相同食材的处理为例描述访问者模式。例如对于鸡,广东厨师会按粤菜做法做成白斩鸡,而四川厨师会做成川菜辣子鸡。对于鱼也类似,广东厨师将其做成清蒸鱼而四川厨师会处理成水煮鱼。可以把厨师描述为访问者,食材是被访问对象,不同的访问者访问相同的对象有不同的结果。具有这种特征的行为模式就是访问者模式。
本示例中,厨师为抽象访问者,广东厨师和四川厨师为具体访问者。被访问事物是鸡和鱼,它们都继承一个抽象访问对象类。广东厨师处理(访问)鸡和鱼会做出白斩鸡和清蒸鱼,而四川厨师处理鸡和鱼会做出辣子鸡和水煮鱼。设置一个访问对象结构类,其内持有所有的访问对象,有管理访问对象的方法add
(增加访问对象)和remove
(移除访问对象)。在客户端声明一个访问对象结构,然后add
具体的访问对象。接着声明具体访问者,调用访问对象结构的访问方法accpet
(传入具体访问者)。最终得到该访问者访问所有访问对象的结果。
package com.yukiyama.pattern.behavior;
import java.util.ArrayList;
import java.util.List;
/**
* 访问者模式
*/
public class VisitorDemo {
public static void main(String[] args) {
// 声明一个访问对象结构实例
ObjectStructure obj = new ObjectStructure();
// 增加访问对象至结构中
obj.add(new ChickenMaterial());
obj.add(new FishMaterial());
// 声明访问者
Cook gdCook = new GuangdongCook();
Cook scCook = new SichuanCook();
// 访问对象结构执行其accept方法,接收具体访问者的访问
// 输出“粤菜白斩鸡”,“粤菜清蒸鱼”
obj.accept(gdCook);
// 输出”川菜辣子鸡“,”川菜水煮鱼“
obj.accept(scCook);
}
}
/**
* 抽象访问者
* 定义对所有访问对象的抽象访问方法,参数是所要访问的对象。
* 下例抽象访问者为厨师,访问对象是鸡和鱼。
*/
abstract class Cook{
public abstract void make(ChickenMaterial chicken);
public abstract void make(FishMaterial fish);
}
/**
* 具体访问者
* 继承抽象访问者类,实现抽象方法。
* 下例是广东厨师访问鸡和鱼的动作,分别处理为粤菜白斩鸡和粤菜清蒸鱼。
*/
class GuangdongCook extends Cook {
@Override
public void make(ChickenMaterial chicken) {
System.out.println("粤菜白斩鸡");
}
@Override
public void make(FishMaterial fish) {
System.out.println("粤菜清蒸鱼");
}
}
/**
* 具体访问者
* 下例是四川厨师访问鸡和鱼的动作,分别处理为川菜辣子鸡和川菜水煮鱼。
*/
class SichuanCook extends Cook {
@Override
public void make(ChickenMaterial chicken) {
System.out.println("川菜辣子鸡");
}
@Override
public void make(FishMaterial fish) {
System.out.println("川菜水煮鱼");
}
}
/**
* 抽象访问对象
* 定义抽象访问方法accept(Cook cook),用于接受一个具体的访问者类的访问。
*/
abstract class Material{
public abstract void accept(Cook cook);
}
/**
* 具体访问对象
* 实现抽象类中的抽象方法accept(Cook cook)。传入具体访问者,调用访问者
* 的行为方法(传入此时的访问对象指针this)。
*/
class ChickenMaterial extends Material{
@Override
public void accept(Cook cook) {
cook.make(this);
}
}
/**
* 具体访问对象
*/
class FishMaterial extends Material{
public void accept(Cook cook) {
cook.make(this);
}
}
/**
* 访问对象结构类
* 持有访问对象的集合List<Material>,管理访问对象,有增加访问对象方法add,
* 移除访问对象方法remove,执行访问者对访问对象的访问动作accept方法。该
* 方法内用for-each遍历该结构类持有的访问对象集合,并对每一个访问对象执行
* 其accpet方法。
*/
class ObjectStructure{
private List<Material> materials = new ArrayList<>();
public void add(Material material) {
if(!materials.contains(material)){
materials.add(material);
}
}
public void remove(Material material) {
if(materials.contains(material)) {
materials.remove(material);
}
}
public void accept(Cook cook) {
for(Material m : materials) {
m.accept(cook);
}
}
}