访问者模式
一、基本定义
1.用于封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
2.属于行为型模式
二、数据结构
- 抽象访问者:要实现对元素的访问,肯定需要获取到相关元素,因此访问者需要有==visit()==方法,并将元素作为参数传递。有几种元素则提供几个visit()方法,因为对不同元素类型的操作是可能不一样的,需要进行不同处理。这也是为什么访问者模式适用于数量稳定的元素实现类,因为如果元素数量增加就需要增加对应的访问处理方法,所有的具体访问者都需要更改。
- 具体访问者:实现抽象访问者,在visit方法中实现对元素对象的具体操作。
- 抽象元素:被访问者,需要实现一个accept方法,用来接受访问者。
- 具体元素:实现抽象元素。
- 结构对象:就是定义里面说的访问者访问操作的结构,首先要有被访问的元素集合,还需要提供接收访问者的方法,在方法内部遍历元素集合,元素实现accept方法接受访问者的访问。
访问者需要visit()方法比较好理解,需要操作哪个元素,肯定需要将元素传入嘛。比较难理解的就是为什么被访问者(也就是元素对象)需要实现一个accept()方法,如果我不实现accept()方法呢,在结构对象里面遍历元素集合的时候直接调用visitor的visit()方法把元素放进去执行不就好了。我是这样理解的,这里的accept()方法是为了让元素在访问者访问自己时提供一个控制,这样有需要的时候我们也可以在元素对象中进行一些代码控制。比如我们有一类非常重要的元素,只让指定的访问者对自己进行操作,可以在对应的accept方法中进行判断,不是指定访问者的一律跳过,保障自己的安全性。
三、适用场景
-
医院的部门和病人
部门可以看成是具体的访问者,病人可以看成是元素对象,现在有一组病人,首先结构对象中维护病人集合,然后每个部门(访问者)调用结构对象对外暴露的方法拿到病人病例,在自己的visit方法中判断自己应该对病人进行什么操作,比如验血,CT,开药,输液等
-
软件包实现对电脑硬件的指令升级
软件包可以看成是具体的访问者,电脑各部分硬件看成是元素对象,元素是稳定不变的,遍历元素的结构对象,软件包可以在自己实现的visit方法中修改各硬件保存的指令数据。
四、代码实现
这里是自己写的一个小案例,将文件元素分为永久和临时,并且基本不会在添加类型,符合结构稳定。
对文件会有很多操作,如添加、删除、更新、迁移、统计等,变化较多,符合操作多变
-
抽象访问者
public interface Visitor { //访问临时文件 void visit(TestSystemFile tsf); //访问永久文件 void visit(PermanentSystemFile psf); }
-
具体访问者(更新文件的访问者)
public class UpdateVisitor implements Visitor{ @Override public void visit(TestSystemFile tsf) { System.out.println(tsf.getName() + "更新了"); } @Override public void visit(PermanentSystemFile psf) { System.out.println(psf.getName() + "更新了"); } }
-
具体访问者(删除文件的访问者)
public class DeleteVisitor implements Visitor{ @Override public void visit(TestSystemFile tsf) { System.out.println(tsf.getName() + "被删除了"); } @Override public void visit(PermanentSystemFile psf) { System.out.println(psf.getName() + "被删除了"); } }
-
抽象元素(系统文件)
abstract class SystemFile { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public SystemFile(String name) { this.name = name; } abstract void accept(Visitor visitor); }
-
具体元素(临时系统文件)
public class TestSystemFile extends SystemFile { public TestSystemFile(String name) { super(name); } @Override public void accept(Visitor visitor) { visitor.visit(this); } }
-
具体元素(永久系统文件)
public class PermanentSystemFile extends SystemFile { public PermanentSystemFile(String name) { super(name); } @Override public void accept(Visitor visitor) { //不允许删除访问者访问自己,对应上文说的元素可以在accept方法中对访问者进行控制 if (!(visitor instanceof DeleteVisitor)) { visitor.visit(this); } } }
-
提供给访问者的元素结构
public class SystemFileConstruct { List<SystemFile> fileList = new ArrayList<>(); public void show(Visitor visitor) { fileList.forEach(file -> { file.accept(visitor); }); } }
-
测试代码
public class TestVisit { public static void main(String[] args) { SystemFileConstruct construct = new SystemFileConstruct(); PermanentSystemFile psf = new PermanentSystemFile("永久一号"); TestSystemFile tsf = new TestSystemFile("临时一号"); construct.fileList.add(psf); construct.fileList.add(tsf); Visitor visitor1 = new UpdateVisitor(); construct.show(visitor1); Visitor visitor2 = new DeleteVisitor(); construct.show(visitor2); } }
-
输出结果
永久一号更新了 临时一号更新了 临时一号被删除了
五、总结
这个案例中,将文件的具体类型与对文件的各种操作进行了解耦,临时文件和永久文件格式确定之后基本不会改变,有一个对外提供,也就是给访问者的[元素结构](# 提供给访问者的元素结构),在测试代码中可以看到添加不同的访问者都实现了对元素列表的访问,并且具体元素中的accept也对访问者控制成功,即永久一号没有被删除。