`
行者买刀
  • 浏览: 194109 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

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

    博客分类:
  • J2SE
阅读更多

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

 

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

分享到:
评论
9 楼 hk8082 2011-02-17  
有点晕晕的感觉,现在我就像半梦半醒,似懂非懂一样。
8 楼 gtssgtss 2011-02-16  
Visitor好像常用于在继承体系中,自动处理向下转型
7 楼 Loudyn 2011-02-16  
<div class="quote_title">行者买刀 写道</div>
<div class="quote_div">
<p>   在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法。但是有时候我们只能看到接口,而根本无法看到其接口实现。这个时候我们就无法往接口里添加接的方法。但是,开发人员能够多大设计的时候采用Visitor模式的话,结果就大不一样了。</p>
<p> </p>
<p><strong>Visitor模式就是让代码用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。</strong></p>
<p><strong></strong></p>
<p>例子:</p>
<p>  关于访问者模式的JE上的例子比较多,而且争议也比较大。下面就举出个常见的Visitor模式的例子,欢迎拍砖。</p>
<p> </p>
<p>如一个公司里有老板,经理,员工三种角色,每个角色都继续Human这个类,对于Human这个类,我们定义为抽象的</p>
<pre name="code" class="java">public abstract class Human {
protected int id;
//该人物所管理的人员,如果是老板那么那就可管理经理
protected List&lt;? extends Human&gt; list = new ArrayList&lt;Human&gt;();

public List&lt;? extends Human&gt; getList() {
return list;
}

public void setList(List&lt;? extends Human&gt; list) {
this.list = list;
}

public int getId() {
return id;
}

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

public  void accept(Visitor visitor){

}
}
</pre>
<p> </p>
<p> </p>
<p>其三种角色都继续这个Human类:</p>
<pre name="code" class="java">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();
}

}</pre>
<p> </p>
<p> </p>
<p> </p>
<p>在我们构造这些公司的成员时, 我假设用如下方法构造:</p>
<pre name="code" class="java">Boss boss = new Boss(1);
List&lt;Manager&gt; managers = new ArrayList&lt;Manager&gt;();
for(int i =2;i&lt;10;i++){
Manager manager = new Manager(i);
List&lt;Employee&gt; employees = new ArrayList&lt;Employee&gt;();
int k = i*10;
for(int j = k;j&lt;k+8;j++){
employees.add(new Employee(j));
}
manager.setList(employees);
managers.add(manager);
}
boss.setList(managers);</pre>
<p> </p>
<p>于是这个时候,我想查询员工号为20的员工的相关信息.当然我可以直接从Boss开始,然后遍历他的List列表,以及子列表。实现如下:</p>
<pre name="code" class="java"> public static Human getHuman(Human human, int id) {
if (human.getId() != id) {
List&lt;Human&gt; ms = (List&lt;Human&gt;) human.getList();
for (Human h : ms) {
if(getHuman(h,id)!=null){
return getHuman(h,id);
}else{
return null;
}
}
return null;
} else {
return human;
}
}</pre>
<p> </p>
<p>但是我们想用访问者模式来实现它,于是我们定义一个访问者接口:</p>
<pre name="code" class="java">/**
* 访问员工接口
* @author Administrator
*
*/
public interface Visitor {
public void visit(Employee employee);
public void visit(Human human);
}
</pre>
<p> </p>
<p>接口实现如下:</p>
<pre name="code" class="java">public class FindVisitor implements Visitor{
private int soughtId;
private Human found;
public void visit(Employee employee) {
if(found==null&amp;&amp;employee.getId()==soughtId){
found=employee;
}
}
public void visit(Human human) {
if(found==null&amp;&amp;human.getId()==soughtId){
found=human;
return;
}
List&lt;? extends Human&gt; 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;
}

}
</pre>
<p> </p>
<p>下面做一下简单的测试吧:</p>
<pre name="code" class="java">FindVisitor fv =new FindVisitor();
Human human = fv.find(boss, 20);
System.out.println("find:"+ human);</pre>
<p> </p>
<p>小结:</p>
<p>   访问者模式可以让我们在不改变类层次结构中的类的情况下,为该类层次结构定义新的操作。如上面的例子,如果想添加新的需求,如要找某个员工号,并修改员工信息,于是我们可以新增接口方法,并添加实现。在类层次结构中添加访问器将调用accept()方法,accept()方法通过采用”两次分开“技术将调用结果返回给访问器类。visit()方法定义在访问器类中,类层次结构中的某个类对象可以根据其类型调用合适的visti()方法。</p>
<p> </p>
<p>   访问器类的开发人员必须清楚将要访问的类层次结构的全部或者部分设计细节。另外,在设计访问器类的时候,我们必须特别注意被访问的对象模型中可能会出现环状结构。考虑到这些问题, 一些开发人员常常会有意避免使个访问者框框。习惯地使用其他方案替换。一般而言,软件开发团队需要根据自己所采用的软件开发方法学,根据项目组以及具体项目的具体情况使用访问者模式 。</p>
</div>
<p>其实我不懂什么visitor模式,我只是觉得Human类这样实现会更好:</p>
<p>
</p>
<pre name="code" class="java">public abstract class Human {
private final int id;
       
        public Human(final int id){
            this.id = id;
        }
//该人物所管理的人员,如果是老板那么那就可管理经理
protected List&lt;? extends Human&gt; list = new ArrayList&lt;Human&gt;();

public List&lt;? extends Human&gt; getList() {
return list;
}

public void setList(List&lt;? extends Human&gt; 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);
}</pre>
 
6 楼 gaoyongbo026 2011-02-15  
基本思想都是把逻辑放在多的一方,因为变化在多的一方
访问者模式可以说假定变的是需求,不变的是用户信息

我也一直搞不明白这个模式,但这两句话让我很有感觉,似乎就是这么回事吧
5 楼 jashawn 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只是一种形式,在很多时候你可以将它倒置,表达你被不同的人访问,基本思想都是把逻辑放在多的一方,因为变化在多的一方。
4 楼 dandy 2010-03-31  
这个例子......
3 楼 行者买刀 2010-03-02  
访问者模式可以说假定变的是需求,不变的是用户信息
2 楼 行者买刀 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);  
    }
1 楼 EyejavaLi 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接口跟实际的类耦合在一起了  不爽!!

相关推荐

    设计模式之略见一斑(Observer观察者模式)

    本篇文章将深入探讨“Observer”观察者模式,这是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 观察者模式的核心概念是主题...

    21种设计模式略见一斑

    《21种设计模式略见一斑》这篇文章和配套的"行者买刀21种设计模式详解.pdf"文件,是面向IT专业人士,尤其是软件开发者的一份珍贵资源,旨在深入理解并掌握软件设计中的核心概念——设计模式。设计模式是经验丰富的...

    CRC16或32校验

    CRC的全称为Cyclic Redundancy Check,中文名称为循环冗余校验。它是一类重要的线性分组码,编码...例如我们读软盘上的文件,以及解压一个ZIP文件时,偶尔会碰到“Bad CRC”错误,由此它在数据存储方面的应用可略见一斑

    2021-2025年中国避雷器行业调研及竞合策略研究报告.pdf

    综上所述,该报告以详细的数据和深入的分析为避雷器行业的企业决策者提供了宝贵的信息,帮助他们了解行业现状、预测未来趋势,并据此制定有效的竞合策略。对于电力行业和相关制造业来说,这份报告不仅能够帮助他们更...

    成语大全带解释.doc

    10. **略见一斑**:形容从局部可以推测到整体的一部分情况。 11. **有口皆碑**:形容人们对某人或某事的赞扬之声普遍且一致。 12. **并行不悖**:两个或多个事情可以同时进行,彼此之间并不冲突。 13. **白璧微瑕**...

    现场活动大屏幕系统v1.1806221.zip

    《现场活动大屏幕系统v1.1806221》是一款专为现场活动设计的多媒体展示系统,它的核心功能在于提供一个高效、稳定且易于操作的大屏幕展示平台。这款系统能够帮助活动组织者在活动现场实时展示各种信息,如活动进程、...

    小学生易错字及生僻字总结.doc

    6. 略见一斑:从一点细微之处就能看出整体的情况或品质。 7. 班门弄斧:在专家面前卖弄自己的技能,暗示自不量力。 8. 英雄辈出:形容一个时代或群体中不断涌现出杰出的人物。 9. 并行不悖:两件事物同时进行而互不...

    曲周事业单位招聘2018年考试真题及答案解析考试版(1).docx

    "略见一斑"意味着事情的端倪已经显现,“光明灿烂”和“辉煌”均形容美好前景,但前者更侧重于未来的可能性,后者侧重于实际的辉煌成就。因此,选B:希望光明,光明璀璨。 8. 经济形势描述:第八题中,“内热外冷”...

    怀念母亲练习题.docx

    - (3) "我当时的想法,从这几段文字中也可以看出一点" 可以替换为 "言外之意,略见一斑"。 2. 动词填空: - ① "暗夜渐渐__上天空,__上对面的屋顶" 可以填入 "爬上,爬满",表达夜晚降临的情景。"一切都__在朦胧...

    高一语文上册第二单元综合测试1[精选].doc

    1. 语文基础知识:题目涉及到词语辨析,如“心酸”与“辛酸往事”,“一般”与“略见一斑”,“过度”与“过渡时期”,“无谓”与“无私无畏”,“家世”与“处理家事”,“明智”与“淡泊明志”,“灌注”与...

    高二语文试卷山东济宁02-03年上学期高二语文期末考试[精选].doc

    3. 字形题:识别并改正错别字,如"略见一斑"、"死不瞑目"、"文过饰非"、"不肖子孙"等。 4. 成语运用:正确使用成语,如"大方之家"、"兵不血刃"、"声誉鹊起"、"噤若寒蝉"等,同时考查成语的含义和适用情境。 5. 词语...

    2015_2016学年高中语文课时训练10雷雨鲁人版必修4

    3. 字形校正:题目中还包含了错别字的纠正,如A项中的“烦燥”应为“烦躁”,“甜言密语”应为“甜言蜜语”,B项中的“半响”应为“半晌”,“略见一班”应为“略见一斑”,C项中的“脉膊”应为“脉搏”,“歪风斜气...

    280个容易读错、写错、用错的成语要点.doc

    12. "略见一斑"(lüè jiàn yī bān):从部分看出整体,通过局部了解全局。 13. "暴虎冯河"(bào hǔ píng hé):形容有勇无谋,冒险行事。 14. "有口皆碑"(yǒukǒu jiē bēi):形容人人称赞,具有很高的...

    小学语文成语大全及其解释.pdf

    10. **略见一斑**:从局部看出事物的一部分特征,了解其大概。 11. **有口皆碑**:所有人都称赞,形容事迹或人品受到普遍赞扬。 12. **并行不悖**:两件事情可以同时进行,互不冲突。 13. **白璧微瑕**:完美的事物...

    容易用错的成语一览表.doc

    4. **黯然失色**:形容相比之下显得差远了,不如别人。同义词有“相形见绌”、“大相径庭”。反义成语如“光彩夺目”、“不相上下”。 5. **慷慨激昂**:形容精神振奋,情绪高昂,充满正气。同义词有“热血沸腾”、...

    小学语文成语大全及其解释.doc

    10. **略见一斑**:通过部分了解整体,形容对事物的初步认识。 11. **有口皆碑**:所有人都称赞,表示对某人或某事的普遍好评。 12. **并行不悖**:两件事情同时进行,并不互相冲突。 13. **白璧微瑕**:比喻美好的...

    高一语文洞察世道沧桑测试[精选].doc

    - 《红楼梦》是中国古代四大名著之一,属于章回体小说,现有120回,前80回由曹雪芹创作,后40回一般认为是高鹗续写。曹雪芹,原名霑,字梦阮。《红楼梦》以贾、史、王、薛四大家族的兴衰为背景,以贾宝玉和林黛玉的...

    CRC算法与实现.doc

    例如,我们读软盘上的文件,以及解压一个 ZIP 文件时,偶尔会碰到“Bad CRC”错误,由此它在数据存储方面的应用可略见一斑。 CRC 的优点 CRC 的优点在于编码和解码方法简单,检错和纠错能力强,在通信领域广泛地...

    年高一年级语文下册第二次段考试卷.doc

    题目2考察学生对汉字书写的准确性,如"烦燥"应为"烦躁"、"甜言密语"应为"甜言蜜语"、"颓垣"、"略见一班"应为"略见一斑"、"莫钟一是一言既出,驷马难追"中"莫钟一是"可能是错误,正确可能是"莫衷一是"。 3. 成语运用...

Global site tag (gtag.js) - Google Analytics