访问者模式
1、定义
封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2、结构图
- 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
- 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
- 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
- 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
- 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
3、代码
(1)Element接口
public interface Element {
public void doSomething();
public void accept(IVisitor visitor);
}
(2)Element1实体
public class ConcreteElement1 implements Element {
@Override
public void doSomething() {
System.out.println("这个是元素1");
}
@Override
public void accept(IVisitor visitor) {
visitor.visite(this);
}
}
(3)Element2实体
public class ConcreteElement2 implements Element {
@Override
public void doSomething() {
System.out.println("这个是元素2");
}
@Override
public void accept(IVisitor visitor) {
visitor.visite(this);
}
}
(4)访问者接口
public interface IVisitor {
public void visite(Element element);
}
(5)访问者实体
public class Visitor implements IVisitor {
@Override
public void visite(Element element) {
element.doSomething();
}
}
(6)结构对象
public class ObjectStruture {
public static List<Element> getElements(){
List<Element> list = new ArrayList<Element>();
Random ran = new Random();
for(int i=0; i<10; i++){
int a = ran.nextInt(100);
if(a>50){
list.add(new ConcreteElement1());
}else{
list.add(new ConcreteElement2());
}
}
return list;
}
}
(7)客户端
public class Client {
public static void main(String[] args) {
List<Element> elements = ObjectStruture.getElements();
IVisitor visitor = new Visitor();
for(Element e: elements){
e.accept(visitor);
}
}
}
(8)运行结果
这个是元素2
这个是元素1
这个是元素2
这个是元素2
这个是元素2
这个是元素1
这个是元素2
这个是元素2
这个是元素1
这个是元素2
注意每次的运行结果都不一样,具有随机性。
4、总结
- 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
- 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。