薛磊 Job Seeker

23种设计模式——23.Visitor 访问者 (行为型模式)


类层次结构的变化

  类层次结构中可能经常由于引入新的操作,从而将类型变得脆弱

动机(Motivation)

  在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。

  如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

意图(Intent)

  表示一个作用于某对象结构中的各元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。

结构(Structure)

  • Visitor:抽象访问者  抽象类或接口,声明访问者可以访问哪些元素。

  • ConcreteVisitor:具体访问者  它影响访问者访问到一个类后该怎么干,要做什么。

  • Element:抽象元素  抽象类或接口,声明接受哪一类访问者访问。

  • ConcreteElement:具体元素  实现accept方法,通常是visitor.visit(this)。

  • ObjectStruture:结构对象  元素产生者,一般容纳在多个不同类、不同接口的容器,在项目中一般很少抽象出这个角色。

    Visitor模式的几个要点

  • Visitor模式通过所谓双重分发来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。

  • 所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。

  • Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Vistor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动“。

示例代码

抽象元素:

public abstract class Element{
	//定义业务逻辑
	public abstract void doSomething();
	//允许谁来访问
	public abstract void accept(IVisitor visitor);
}

具体元素:

public class ConcreteElement1 extends Element{
	//完善业务逻辑
	public void doSomething(){
		//业务处理
	}
	public void accept(IVisitor visitor)[
		visitor.visit(this);
	}
}

public class ConcreteElement2 extends Element{
	//完善业务逻辑
	public void doSomething(){
		//业务处理
	}
	public void accept(IVisitor visitor)[
		visitor.visit(this);
	}
}

抽象访问者:

public interface IVisitor{
	//可以访问哪些对象
	public void visit(ConcreteElement el1);
	public void visit(CpncreteElement el2);
}

具体访问者:

public class Visitor implements IVisitor{
	//访问el1元素
	public void visit(ConcreteElement1 el1){
		el1.doSomething();
	}
	//访问el2元素
	public void visit(ConcreteElement2 el2){
		el2.doSomething();
	}
}	

结构对象:

public class ObjectStruture{
	//对象生成器,这里通过一个工厂方法模式模拟
	public static Element createElement(){
		Random rand = new Random();
		if(rand.nextInt(100)>50){
			return new ConcreteElement1();
		}else{
			return new ConcreteElement2()
		}
	}
}

场景类:

public class Client{
	public static void main(String[] args){
		for(int i=0;i<10;i++){
			//获得元素对象
			Element el = ObjectStruture.createElement();//接受访问者访问
			el.accept(new Visitor());		
		}
	}
}

Comments

Content