职业IT人-IT人生活圈

 找回密码
 成为会员
搜索
查看: 464|回复: 9

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

[复制链接]
月上萧萧 发表于 2011-9-1 11:48 | 显示全部楼层 |阅读模式
    在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用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){   
           
    }   
}  

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

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

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

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

/**
* 访问员工接口
* @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;   
    }   
      
}  

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

FindVisitor fv =new FindVisitor();
Human human = fv.find(boss, 20);
System.out.println("find:"+ human);
小结:
   访问者模式可以让我们在不改变类层次结构中的类的情况下,为该类层次结构定义新的操作。如上面的例子,如果想添加新的需求,如要找某个员工号,并修改员工信息,于是我们可以新增接口方法,并添加实现。在类层次结构中添加访问器将调用accept()方法,accept()方法通过采用”两次分开“技术将调用结果返回给访问器类。visit()方法定义在访问器类中,类层次结构中的某个类对象可以根据其类型调用合适的visti()方法。

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

shmilyyu 发表于 2011-9-1 11:48 | 显示全部楼层
被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接口跟实际的类耦合在一起了  不爽!!

gz-vps 发表于 2011-9-1 11:48 | 显示全部楼层
老板,经理,员工
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();      
    }      
     
}   

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

北大青鸟 发表于 2011-9-1 11:48 | 显示全部楼层
访问者模式可以说假定变的是需求,不变的是用户信息

shmilyyu 发表于 2011-9-1 11:49 | 显示全部楼层
这个例子......

有烟没火 发表于 2011-9-1 11:49 | 显示全部楼层
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只是一种形式,在很多时候你可以将它倒置,表达你被不同的人访问,基本思想都是把逻辑放在多的一方,因为变化在多的一方。

ksdal 发表于 2011-9-1 11:49 | 显示全部楼层
基本思想都是把逻辑放在多的一方,因为变化在多的一方
访问者模式可以说假定变的是需求,不变的是用户信息

我也一直搞不明白这个模式,但这两句话让我很有感觉,似乎就是这么回事吧

有烟没火 发表于 2011-9-1 11:49 | 显示全部楼层
行者买刀 写道
   在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用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){   
           
    }   
}  

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

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

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

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

/**
* 访问员工接口
* @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;   
    }   
      
}  

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

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

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

只学java 发表于 2011-9-1 11:49 | 显示全部楼层
Visitor好像常用于在继承体系中,自动处理向下转型

fl 发表于 2011-9-1 11:49 | 显示全部楼层
有点晕晕的感觉,现在我就像半梦半醒,似懂非懂一样。
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

QQ|手机版|小黑屋|网站帮助|职业IT人-IT人生活圈 ( 粤ICP备12053935号-1 )|网站地图
本站文章版权归原发布者及原出处所有。内容为作者个人观点,并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是信息平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽造成漏登,请及时联系我们,我们将根据著作权人的要求立即更正或者删除有关内容。

GMT+8, 2024-5-5 09:23 , Processed in 0.121264 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表