目录
一、基础概念
二、UML类图
三、角色设计
四、案例分析
五、总结
访问者模式是一种对象行为型设计模式,它能够在不修改已有对象结构的前提下,为对象结构中的每个对象提供新的操作。
访问者模式的主要作用是把对元素对象的操作抽象出来封装到访问者类中,这样就可以对存在不同操作的元素对象进行统一的处理。
角色 | 描述 |
---|---|
抽象访问者角色 | 定义访问元素对象的操作,每个操作对应一个方法 |
具体访问者角色 | 实现访问者接口,给出对每个元素类型访问的具体实现 |
抽象元素角色 | 定义一个含有接受操作方法accept()的接口,accept()方法以一个访问者对象为参数 |
具体元素角色 | 实现抽象元素接口,每个具体元素都要实现accept()方法,在accept()方法中调用访问者对象的对应方法 |
对象结构角色 | 可以枚举它的元素,可以提供高层的接口以允许访问者访问它的元素 |
下面通过一段代码演示如何使用访问者模式来对不同类型的资源文件(Image、Video、Document)进行压缩操作。
客户端可以为ResourceFileStructure添加不同的资源文件,然后传入一个Compressor访问者对象,就可以通过统一的访问接口对不同资源文件进行压缩操作,而不需要修改资源文件类的代码。
定义Compressor接口(抽象访问者角色),声明了压缩每一种资源文件的方法 :
public interface Compressor {
void compress(Image image);
void compress(Video video);
void compress(Document document);
}
ZipCompressor类(具体访问者角色),实现了压缩每一种资源文件的具体操作:
public class ZipCompressor implements Compressor {
@Override
public void compress(Image image) {
System.out.println("压缩了图片:"+image.getName());
}
@Override
public void compress(Video video) {
System.out.println("压缩了视频:"+video.getName());
}
@Override
public void compress(Document document) {
System.out.println("压缩了文档:"+document.getName());
}
}
ResourceFile接口(抽象元素角色),声明了接受访问者的accept方法:
public interface ResourceFile {
void accept(Compressor compressor);
}
Image、Video、Document类(具体元素角色),提供了资源文件信息,并在accept方法中调用访问者的对应方法:
public class Image implements ResourceFile {
private String name;
public Image(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Compressor compressor) {
compressor.compress(this);
}
}
public class Video implements ResourceFile {
private String name;
public Video(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Compressor compressor) {
compressor.compress(this);
}
}
public class Document implements ResourceFile {
private String name;
public Document(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Compressor compressor) {
compressor.compress(this);
}
}
ResourceFileStructure类(对象结构角色)管理所有元素,并可以遍历元素使其接受访问者访问:
import java.util.ArrayList;
import java.util.List;
public class ResourceFileStructure {
private List files = new ArrayList<>();
public void addResource(ResourceFile file) {
files.add(file);
}
public void handleVisiting(Compressor compressor) {
for(ResourceFile file : files) {
// 调用元素的accept方法
file.accept(compressor);
}
}
}
客户端:
public class Client {
public static void main(String[] args) {
// 客户端代码
ResourceFileStructure structure = new ResourceFileStructure();
// 添加资源文件
structure.addResource(new Image("a.png"));
structure.addResource(new Video("b.mp4"));
structure.addResource(new Document("c.doc"));
Compressor compressor = new ZipCompressor();
// 处理访问
structure.handleVisiting(compressor);
// 输出结果
System.out.println("文件压缩完成");
}
}
运行结果如下:
该案例的执行流程是:
1、创建资源文件对象(Image、Video、Document等)和资源文件结构ResourceFileStructure对象。
2、向ResourceFileStructure对象中添加资源文件对象。
3、创建具体访问者对象,例如ZipCompressor。
4、调用ResourceFileStructure的handleVisiting()方法,传入访问者对象。
5、在handleVisiting()方法中遍历内部存储的所有资源文件,并调用每个资源文件的accept()方法,以访问者对象作为参数。
6、在每个资源文件类的accept()方法中,调用访问者对象的参数对应的方法,例如调用compressor.compress(this)。
7、于是压缩程序类中的compress()方法就会被执行,从而实现对该资源文件的压缩操作。
8、这样就通过访问者对象使不同类型的资源文件进行了统一的压缩处理,而不需要修改资源文件类的代码。
9、如果需要新增一种压缩方式,只需要新增一个实现Compressor接口的类,不需要修改资源文件类。
优点:
1、增加新的操作很容易。访问者模式使得新增操作变得很容易。如果需要增加新的操作,只需要添加一个新的具体访问者类即可,无需作用于其他类的修改。
2、实现了数据结构与操作分离。访问者模式将数据结构与作用于结构上的操作分离开来,使得操作集合可相对自由地演化而不影响系统的数据结构。
3、符合单一职责原则。访问者模式把相关的行为封装到一个访问者对象中,使每个访问者的功能都比较单一。
4、扩展性良好。可以在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
缺点:
1、具体元素对访问者公布细节。访问者模式中具体元素对访问者公布自己的细节,这打破了对象的封装性。
2、具体元素变更困难。如果一个对象结构中的具体元素发生变化,例如新增或者删除了元素,那么访问者都需要进行适当的修改。
3、违反了依赖倒置原则。访问者依赖了具体元素,而不是抽象元素。
4、破坏对象结构的封装。访问者模式中对象结构必须公布自身的内部细节,否则访问者无法访问对象内部的元素,这破坏了对象的封装性。
应用场景:
1、对象结构稳定,但其操作经常变化的系统。访问者模式可以隔离变化的部分。
2、需要对对象结构的对象执行很多不同和不相关的操作,可以将这些操作集中到访问者中,避免污染对象类。
3、希望新增操作时不改变对象结构,新增访问者可以实现此需求。
4、需要对不同对象结构执行共同操作,可以使用访问者模式重用操作。
5、对象结构中对象类型经常改变,访问者模式可以隔离这种变化对对象结构的影响。
6、在不改变对象类的情况下,为对象增加新功能。
7、将相关操作集中到访问者中,避免将操作分散到对象类中。
8、将变化点从稳定的对象结构中抽象出来,提高软件灵活性。
符合的设计原则:
1、单一职责原则(Single Responsibility Principle)
访问者模式通过将相关行为抽取到访问者类中,使每个访问者类都只负责一组特定的行为,这点符合单一职责原则。
访问者模式适用于数据结构相对稳定,但经常需要在此数据结构上定义新的操作的时候使用。如果数据结构经常变化,或者都操作不太发生变化,则不太适合使用访问者模式。