我们下面通过一个解压和压缩各种类型的文件的案例来说明访问者模式的使用。
在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随
着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者
对象就可以处理元素对象上的操作。
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这
些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一
个判断,这就是访问者模式。
优点:1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点:1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒
置原则,依赖了具体类,没有依赖抽象。
使用场景:1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一
个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希
望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
适用性:
一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,让你想避免让这些操作污染这些对象的类。
Visitor使得你可以将相关的操作集中起来定义在一个类中,当该对象结构被很多应用共享时,用Visitor模
式让每个应用仅包含需要用到的操作。
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者
的接口,这可能想要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
package visitor
// ========== 访问者FileUseVisitor ==========
type FileUseVisitor interface {
// 为每一个类声明一个visit操作
visitPdfFile(FileResourceVisitable)
visitPPTFile(FileResourceVisitable)
visitTextFile(FileResourceVisitable)
}
package visitor
import "fmt"
// ========== 访问者Compress ==========
type Compress struct {
}
func (compress *Compress) visitPdfFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Compress file: " + fileResourceVisitable.(*PdfFile).Path)
}
func (compress *Compress) visitPPTFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Compress file: " + fileResourceVisitable.(*PPTFile).Path)
}
func (compress *Compress) visitTextFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Compress file: " + fileResourceVisitable.(*TextFile).Path)
}
package visitor
import "fmt"
// ========== 访问者Decompress ==========
type Decompress struct {
}
func (decompress *Decompress) visitPdfFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Decompress file: " + fileResourceVisitable.(*PdfFile).Path)
}
func (decompress *Decompress) visitPPTFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Decompress file: " + fileResourceVisitable.(*PPTFile).Path)
}
func (decompress *Decompress) visitTextFile(fileResourceVisitable FileResourceVisitable) {
fmt.Println("Decompress file: " + fileResourceVisitable.(*TextFile).Path)
}
package visitor
// ========== 接收者FileResourceVisitable ==========
type FileResourceVisitable interface {
accept(FileUseVisitor)
}
package visitor
// ========== 接收者PdfFile ==========
type PdfFile struct {
Path string
}
func (pdfFile *PdfFile) accept(fileUseVisitor FileUseVisitor) {
fileUseVisitor.visitPdfFile(pdfFile)
}
package visitor
// ========== 接收者PPTFile ==========
type PPTFile struct {
Path string
}
func (pPTFile *PPTFile) accept(fileUseVisitor FileUseVisitor) {
fileUseVisitor.visitPPTFile(pPTFile)
}
package visitor
// ========== 接收者TextFile ==========
type TextFile struct {
Path string
}
func (textFile *TextFile) accept(fileUseVisitor FileUseVisitor) {
fileUseVisitor.visitTextFile(textFile)
}
package visitor
// ========== FileStructure ==========
type FileStructure struct {
fileResourceVisitableList []FileResourceVisitable
}
func (fileStructure *FileStructure) Attach(fileResourceVisitable FileResourceVisitable) {
fileStructure.fileResourceVisitableList = append(fileStructure.fileResourceVisitableList, fileResourceVisitable)
}
func (fileStructure *FileStructure) Detach(fileResourceVisitable FileResourceVisitable) {
for i := 0; i < len(fileStructure.fileResourceVisitableList); i++ {
if fileStructure.fileResourceVisitableList[i] == fileResourceVisitable {
fileStructure.fileResourceVisitableList = append(fileStructure.fileResourceVisitableList[:i], fileStructure.fileResourceVisitableList[i+1:]...)
}
}
}
func (fileStructure *FileStructure) Accept(fileUseVisitor FileUseVisitor) {
for _, fileResourceVisitable := range fileStructure.fileResourceVisitableList {
fileResourceVisitable.accept(fileUseVisitor)
}
}
package main
import . "proj/visitor"
func main() {
fileStructure := FileStructure{}
fileStructure.Attach(&PPTFile{Path: "test.ppt"})
fileStructure.Attach(&PdfFile{Path: "test.pdf"})
fileStructure.Attach(&TextFile{Path: "test.txt"})
fileStructure.Accept(&Decompress{})
fileStructure.Accept(&Compress{})
}
# 输出
Decompress file: test.ppt
Decompress file: test.pdf
Decompress file: test.txt
Compress file: test.ppt
Compress file: test.pdf
Compress file: test.txt
package com.visitor;
// ========== 访问者FileUseVisitor ==========
public interface FileUseVisitor {
// 为每一个类声明一个visit操作
void visitPdfFile(FileResourceVisitable fileResourceVisitable);
void visitPPTFile(FileResourceVisitable fileResourceVisitable);
void visitTextFile(FileResourceVisitable fileResourceVisitable);
}
package com.visitor;
// ========== 访问者Compress ==========
public class Compress implements FileUseVisitor{
@Override
public void visitPdfFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Compress file: "+fileResourceVisitable.path);
}
@Override
public void visitPPTFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Compress file: "+fileResourceVisitable.path);
}
@Override
public void visitTextFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Compress file: "+fileResourceVisitable.path);
}
}
package com.visitor;
// ========== 访问者Decompress ==========
public class Decompress implements FileUseVisitor{
@Override
public void visitPdfFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Decompress file: "+fileResourceVisitable.path);
}
@Override
public void visitPPTFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Decompress file: "+fileResourceVisitable.path);
}
@Override
public void visitTextFile(FileResourceVisitable fileResourceVisitable) {
System.out.println("Decompress file: "+fileResourceVisitable.path);
}
}
package com.visitor;
// ========== 接收者FileResourceVisitable ==========
public abstract class FileResourceVisitable {
protected String path;
abstract void accept(FileUseVisitor fileUseVisitor);
}
package com.visitor;
// ========== 接收者PdfFile ==========
public class PdfFile extends FileResourceVisitable {
public PdfFile(String path){
this.path = path;
}
@Override
public void accept(FileUseVisitor fileUseVisitor) {
fileUseVisitor.visitPdfFile(this);
}
}
package com.visitor;
// ========== 接收者PPTFile ==========
public class PPTFile extends FileResourceVisitable {
public PPTFile(String path){
this.path = path;
}
@Override
public void accept(FileUseVisitor fileUseVisitor) {
fileUseVisitor.visitPPTFile(this);
}
}
package com.visitor;
// ========== 接收者TextFile ==========
public class TextFile extends FileResourceVisitable {
public TextFile(String path){
this.path = path;
}
@Override
public void accept(FileUseVisitor fileUseVisitor) {
fileUseVisitor.visitTextFile(this);
}
}
package com.visitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
// ========== FileStructure ==========
public class FileStructure {
List<FileResourceVisitable> fileResourceVisitableList = new ArrayList<>();
public void attach(FileResourceVisitable fileResourceVisitable){
fileResourceVisitableList.add(fileResourceVisitable);
}
public void detach(FileResourceVisitable fileResourceVisitable){
Objects.requireNonNull(fileResourceVisitable);
}
public void accept(FileUseVisitor fileUseVisitor){
for(FileResourceVisitable fileResourceVisitable:fileResourceVisitableList){
fileResourceVisitable.accept(fileUseVisitor);
}
}
}
package com.visitor;
public class Test {
public static void main(String[] args) {
FileStructure fileStructure = new FileStructure();
fileStructure.attach(new PPTFile("test.pdf"));
fileStructure.attach(new PdfFile("test.pdf"));
fileStructure.attach(new TextFile("test.txt"));
fileStructure.accept(new Compress());
fileStructure.accept(new Decompress());
}
}
# 输出
Compress file: test.pdf
Compress file: test.pdf
Compress file: test.txt
Decompress file: test.pdf
Decompress file: test.pdf
Decompress file: test.txt