理解访问者模式

  访问者模式是个有点深度的模式。

  先看维基的描述:

  访问者模式是一种将算法与对象结构分离的软件设计模式。

  这个模式的基本想法如下:

   1. 首先我们拥有一个由许多对象构成的对象结构,这些对象的类都拥有一个accept方法用来接受访问者对象;

   2.访问者是一个接口,它拥有一个visit方法,这个方法对访问到的对象结构中不同类型的元素作出不同的反应;

   3.在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施accept方法,在每一个元素的accept方法中回调访问者的visit方法,从而使访问者得以处理对象结构的每一个元素。

   4.我们可以针对对象结构设计不同的实在的访问者类来完成不同的操作。 

 

  当然还有一点作用它没提到,visitor模式可以在不改变对象结构现有类的情况下(不污染),给它们加上新的方法,不过这个不是visitor模式的重点。

 

我的理解:

  设想某个对象结构中,存在一些不同的实例,它们实现一个共同接口,而具体行为可能完全不同。如果要遍历结构中的对象,并做某种处理,恐怕要用if instanceof  来判断实例类型,再做处理,因为访问者(算法拥有者)并不知道那些对象的具体类型。如此一来,对象结构和算法就不可避免耦合。

 

  访问者模式的解决办法很简单,既然访问者无法直接判断对象结构中对象的类型,那就把对象实例作为参数传给访问者。而访问者中定义一系列重载函数,根据具体传入实例类型来分派执行。

 

  那么问题就是怎么将对象结构中的对象作为实例传递给访问者呢?

 

  最简单办法就是在遍历过程中 访问者.visit(被访问对象),简单明了。

 

  但是这样又会产生一个问题,被访问对象的类型是无需关心了,访问者对象的类型怎么办?用多个访问者处理对象结构也是很正常的事吧?

 

  无需关心被访问者类型,也无需关心访问者类型,实现完美的OCP,访问者模式提供了解决方式。换个说法叫"双重分派"  

 

自己写个Demo:




 
 

 army中有各种职业:将军,牧师,士兵,

 有2个算法基于army实现:打仗,吃饭

 

army成员:

public interface IArmy {
	public String fight();	
	public void accept(IVisitor visitor);	
}

 

public class General implements IArmy {
	private String role = "将军";
	
	private String eat = "火腿";

	public String getEat() {
		return eat;
	}

	public String getRole() {
		return role;
	}

	@Override
	public String fight() {
		return this.getRole()+":"+"给我上";		
	}

	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}

}

 

public class Minister implements IArmy {
	private String role = "牧师";
	
	private String eat = "蓝瓶";

	public String getEat() {
		return eat;
	}

	public String getRole() {
		return role;
	}
	
	@Override
	public String fight() {
		return this.getRole()+":"+"我奶";		
	}
	
	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}

}

 

public class Soldier implements IArmy {
	private String role = "士兵";
	
	private String eat = "面包";

	public String getEat() {
		return eat;
	}

	public String getRole() {
		return role;
	}
	
	@Override
	public String fight() {
		return this.getRole()+":"+"我砍";	
	}
	
	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}

}

 visitor

public interface IVisitor {
	public void visit(General general);
	public void visit(Minister minister);
	public void visit(Soldier soldier);
}

 

public class FightVisitor implements IVisitor {

	@Override
	public void visit(General general) {
		System.out.println(general.fight());
	}

	@Override
	public void visit(Minister minister) {
		System.out.println(minister.fight());	
	}

	@Override
	public void visit(Soldier soldier) {
		System.out.println(soldier.fight());	
	}

}

 

 

package vistor;

public class SupplyVisitor implements IVisitor {

	@Override
	public void visit(General general) {
		System.out.println(general.getEat());
	}

	@Override
	public void visit(Minister minister) {
		System.out.println(minister.getEat());
	}

	@Override
	public void visit(Soldier soldier) {
		System.out.println(soldier.getEat());
	}

}

 Army(对象结构):

public class Army {
	private List<IArmy> army = new ArrayList<IArmy>();
	
	public void add(IArmy member){
		this.army.add(member);
	}

	public void handleRequest(IVisitor visitor) {
		for (IArmy member : army){
			member.accept(visitor);
		}	
	}
}

 

client:

public class Client {

	public static void main(String[] args) {
		Army army = new Army();
		army.add(new General());
		army.add(new Minister());
		army.add(new Soldier());
		//补给
		army.handleRequest(new SupplyVisitor());
		//战斗
		army.handleRequest(new FightVisitor());
	}

}

 结果:

火腿
蓝瓶
面包
将军:给我上
牧师:我奶
士兵:我砍

 

这个实现过程有点象踢皮球:

    1.对象结构(army)接收访问者对象,不关心其具体类型

           public void handleRequest(IVisitor visitor){...}

    2.对象结构(army)中遍历被访问对象时,被访问对象接收访问者对象

           member.accept(visitor);

    3.被访问对象把自己作为参数交给访问者并调用访问者的方法

             public void accept(IVisitor visitor) {

  visitor.visit(this);

       }

     4.访问者对象执行算法,可以利用被访问者对象的方法,也可以自定义新方法。

              

这个模式让我有点晕天真

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值