在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用Visitor模式的话,结果就大不一样了。
Visitor模式就是让代码用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。
例子:
关于访问者模式的JE上的例子比较多,而且争议也比较大。下面就举出个常见的Visitor模式的例子,欢迎拍砖。
如一个公司里有老板,经理,员工三种角色,每个角色都继续Human这个类,对于Human这个类,我们定义为抽象的
public abstract class Human {
protected int id;
//该人物所管理的人员,如果是老板那么那就可管理经理
protected List<? extends Human> list = new ArrayList<Human>();
public List<? extends Human> getList() {
return list;
}
public void setList(List<? extends Human> list) {
this.list = list;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void accept(Visitor visitor){
}
}
其三种角色都继续这个Human类:
public class Boss extends Human {
public Boss(int id){
this.id = id;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String toString(){
return new StringBuffer("Manager the id:"+id).toString();
}
}
public class Manager extends Human {
public Manager(int id){
this.id = id;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String toString(){
return new StringBuffer("Manager the id:"+id).toString();
}
}
public class Employee extends Human{
public Employee(int id){
this.id = id;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String toString(){
return new StringBuffer("Employee the id:"+id).toString();
}
}
在我们构造这些公司的成员时, 我假设用如下方法构造:
Boss boss = new Boss(1);
List<Manager> managers = new ArrayList<Manager>();
for(int i =2;i<10;i++){
Manager manager = new Manager(i);
List<Employee> employees = new ArrayList<Employee>();
int k = i*10;
for(int j = k;j<k+8;j++){
employees.add(new Employee(j));
}
manager.setList(employees);
managers.add(manager);
}
boss.setList(managers);
于是这个时候,我想查询员工号为20的员工的相关信息.当然我可以直接从Boss开始,然后遍历他的List列表,以及子列表。实现如下:
public static Human getHuman(Human human, int id) {
if (human.getId() != id) {
List<Human> ms = (List<Human>) human.getList();
for (Human h : ms) {
if(getHuman(h,id)!=null){
return getHuman(h,id);
}else{
return null;
}
}
return null;
} else {
return human;
}
}
但是我们想用访问者模式来实现它,于是我们定义一个访问者接口:
/**
* 访问员工接口
* @author Administrator
*
*/
public interface Visitor {
public void visit(Employee employee);
public void visit(Human human);
}
接口实现如下:
public class FindVisitor implements Visitor{
private int soughtId;
private Human found;
public void visit(Employee employee) {
if(found==null&&employee.getId()==soughtId){
found=employee;
}
}
public void visit(Human human) {
if(found==null&&human.getId()==soughtId){
found=human;
return;
}
List<? extends Human> list = human.getList();
for(Human e:list){
if(found==null)
e.accept(this);
}
}
public Human find(Human mc,int id){
found = null;
soughtId = id;
mc.accept(this);
return found;
}
}
下面做一下简单的测试吧:
FindVisitor fv =new FindVisitor();
Human human = fv.find(boss, 20);
System.out.println("find:"+ human);
小结:
访问者模式可以让我们在不改变类层次结构中的类的情况下,为该类层次结构定义新的操作。如上面的例子,如果想添加新的需求,如要找某个员工号,并修改员工信息,于是我们可以新增接口方法,并添加实现。在类层次结构中添加访问器将调用accept()方法,accept()方法通过采用”两次分开“技术将调用结果返回给访问器类。visit()方法定义在访问器类中,类层次结构中的某个类对象可以根据其类型调用合适的visti()方法。
访问器类的开发人员必须清楚将要访问的类层次结构的全部或者部分设计细节。另外,在设计访问器类的时候,我们必须特别注意被访问的对象模型中可能会出现环状结构。考虑到这些问题, 一些开发人员常常会有意避免使个访问者框框。习惯地使用其他方案替换。一般而言,软件开发团队需要根据自己所采用的软件开发方法学,根据项目组以及具体项目的具体情况使用访问者模式 。