论坛首页 Java企业应用论坛

设计模式之略见一斑(Visitor访问者模式)

浏览 4018 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-02-25  

   在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用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()方法。

 

   访问器类的开发人员必须清楚将要访问的类层次结构的全部或者部分设计细节。另外,在设计访问器类的时候,我们必须特别注意被访问的对象模型中可能会出现环状结构。考虑到这些问题, 一些开发人员常常会有意避免使个访问者框框。习惯地使用其他方案替换。一般而言,软件开发团队需要根据自己所采用的软件开发方法学,根据项目组以及具体项目的具体情况使用访问者模式 。

   发表时间:2010-03-02  
被lz的例子搞晕了,我在试着学习设计模式

有需要请教的地方:

不知道
#  public  void accept(Visitor visitor){ 
#          
#     } 
这个方法有什么存在的理由

#  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);  把这里改成visit(e);效果是一样的
#         } 
#     } 

lz的这个例子是不是有问题?我总觉得这个visitor接口跟实际的类耦合在一起了  不爽!!
0 请登录后投票
   发表时间:2010-03-02  
老板,经理,员工
EyejavaLi 写道
被lz的例子搞晕了,我在试着学习设计模式

有需要请教的地方:

不知道
#  public  void accept(Visitor visitor){ 
#          
#     } 
这个方法有什么存在的理由

#  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);  把这里改成visit(e);效果是一样的
#         } 
#     } 

lz的这个例子是不是有问题?我总觉得这个visitor接口跟实际的类耦合在一起了  不爽!!

假定老板,经理,员工这三个角色都有自己的特有的东西,如工资,并且是不对外的,也就说没有getXX()方法,现在要工资调查组(FindVisitor )要调查他们的工资,并且不让彼此知道,要怎么办?。。。。

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();   
    }   
  
}  


这个时候可以在上面的
@Override 
    public void accept(Visitor visitor) {  
    //。。。可以在这里通过接口返回调查组所需要的工资
    visitor.visit(this);  
    }
0 请登录后投票
   发表时间:2010-03-02  
访问者模式可以说假定变的是需求,不变的是用户信息
0 请登录后投票
   发表时间:2010-03-31  
这个例子......
0 请登录后投票
   发表时间:2010-03-31  
EyejavaLi 写道
被lz的例子搞晕了,我在试着学习设计模式

有需要请教的地方:

不知道
#  public  void accept(Visitor visitor){ 
#          
#     } 
这个方法有什么存在的理由

#  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);  把这里改成visit(e);效果是一样的
#         } 
#     } 

lz的这个例子是不是有问题?我总觉得这个visitor接口跟实际的类耦合在一起了  不爽!!


结果是一样的,但设计的时候会发现全不一样。
1. 从封闭性上讲,对象都有对外公开的东西,还有不公开的东西,
e就是被访问者,this就是访问者, e.accept(this)这个调用表示出访问的逻辑将在被访问者中实现,它可以很好地隐藏被访问者的私有内容,visit(e)意味着e必须公开大量信息给this,而this将被众多的e所公开的不同信息搞得焦头烂额。

2. 从模型建讲,它表达一对多的关系,它非常符合现实,一份资源总是被很多其它东西共享,比如一份文件,有人会把它下载下来,有人需要在页面上显示它的基本信息,还有人可能要把它emails给别人,你不可能把这些不同的逻辑都写在文件类里,因为文件生来就有,它不知道将来会有谁来使用自己。类似的例子,还有经理在每个年底访问自己的手下,访问者模式可以把访问过程从经理这里分享给手下,从而应对不同的手下。
accept和visit只是一种形式,在很多时候你可以将它倒置,表达你被不同的人访问,基本思想都是把逻辑放在多的一方,因为变化在多的一方。
0 请登录后投票
   发表时间:2011-02-15  
基本思想都是把逻辑放在多的一方,因为变化在多的一方
访问者模式可以说假定变的是需求,不变的是用户信息

我也一直搞不明白这个模式,但这两句话让我很有感觉,似乎就是这么回事吧
0 请登录后投票
   发表时间:2011-02-16  
行者买刀 写道

   在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用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()方法。

 

   访问器类的开发人员必须清楚将要访问的类层次结构的全部或者部分设计细节。另外,在设计访问器类的时候,我们必须特别注意被访问的对象模型中可能会出现环状结构。考虑到这些问题, 一些开发人员常常会有意避免使个访问者框框。习惯地使用其他方案替换。一般而言,软件开发团队需要根据自己所采用的软件开发方法学,根据项目组以及具体项目的具体情况使用访问者模式 。

其实我不懂什么visitor模式,我只是觉得Human类这样实现会更好:

public abstract class Human {
	private final int id;
        
        public Human(final int id){
            this.id = 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 final int getId() {
		return id;
	}

	public final void setId(int id) {
		this.id = id;
	}

	public  abstract void accept(Visitor visitor);
}
 
0 请登录后投票
   发表时间:2011-02-16  
Visitor好像常用于在继承体系中,自动处理向下转型
0 请登录后投票
   发表时间:2011-02-17  
有点晕晕的感觉,现在我就像半梦半醒,似懂非懂一样。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics