作者:Grey
原文地址:
访问者模式是一种行为型模式。
访问者模式在结构不变的情况下动态改变对于内部元素的动作。
举例说明:
假设我们需要构造一台电脑,有主板( Board ),CPU ,内存( Memory ),但是针对企业用户和个人用户,电脑组件的价格是不一样的,我们需要根据不同客户获取一台电脑的总价格。
我们先抽象出电脑组件这个类
public abstract class ComputerPart {
abstract void accept(Visitor visitor);
abstract int getPrice();
}
每个具体组件会继承这个抽象类,以主板( Board )为例
public class Board extends ComputerPart {
@Override
void accept(Visitor visitor) {
visitor.visitBoard(this);
}
@Override
int getPrice() {
return 20;
}
}
抽象出一个访问者( Visitor )接口,
public interface Visitor {
void visitCPU(CPU cpu);
void visitBoard(Board board);
void visitMemory(Memory memory);
}
每个具体类型的访问者实现这个接口,然后定义其不同的价格策略,以公司访问者为例( CorpVisitor )
public class CorpVisitor implements Visitor {
private int totalPrice;
@Override
public void visitCPU(CPU cpu) {
totalPrice += cpu.getPrice() - 1;
}
@Override
public void visitBoard(Board board) {
totalPrice += board.getPrice() - 2;
}
@Override
public void visitMemory(Memory memory) {
totalPrice += memory.getPrice() - 3;
}
public int getTotalPrice() {
return totalPrice;
}
}
个人访问者( PersonalVisitor )类似
public class PersonalVisitor implements Visitor {
private int totalPrice;
@Override
public void visitCPU(CPU cpu) {
totalPrice += cpu.getPrice() + 1;
}
@Override
public void visitBoard(Board board) {
totalPrice += board.getPrice() + 2;
}
@Override
public void visitMemory(Memory memory) {
totalPrice += memory.getPrice() + 3;
}
public int getTotalPrice() {
return totalPrice;
}
}
主方法调用方式如下
public class Main {
public static void main(String[] args) {
ComputerPart cpu = new CPU();
ComputerPart memory = new Memory();
ComputerPart board = new Board();
PersonalVisitor personalVisitor = new PersonalVisitor();
cpu.accept(personalVisitor);
memory.accept(personalVisitor);
board.accept(personalVisitor);
System.out.println(personalVisitor.getTotalPrice());
ComputerPart cpu2 = new CPU();
ComputerPart memory2 = new Memory();
ComputerPart board2 = new Board();
CorpVisitor corpVisitor = new CorpVisitor();
cpu2.accept(corpVisitor);
memory2.accept(corpVisitor);
board2.accept(corpVisitor);
System.out.println(corpVisitor.getTotalPrice());
}
}
可以看到,不同的访问者,对于电脑的价格是不一样的。
上述示例的 UML 图如下
访问者模式的应用
Java SE 中的 FileVisitor 使用了访问者模式,使用示例
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
/**
* @author <a href="mailto:410486047@qq.com">GreyZeng</a>
* @version 1.0, 2022/8/11
*/
public class FileVisitorTest {
public static void main(String[] args) throws IOException {
// 使用FileVisitor对目录进行遍历
// 访问当前目录的所有文件
Files.walkFileTree(Paths.get("."), new SimpleFileVisitor<Path>() {
// 在访问子目录前触发该方法
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("正在访问" + dir + "目录");
return FileVisitResult.CONTINUE;
}
// 在访问文件时触发该方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("正在访问" + file + "文件");
return FileVisitResult.CONTINUE;
}
// 在访问失败时触发该方法
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 写一些具体的业务逻辑
return super.visitFileFailed(file, exc);
}
// 在访问目录之后触发该方法
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// 写一些具体的业务逻辑
return super.postVisitDirectory(dir, exc);
}
});
}
}
其他应用
做编译器的时候,需要生成 AST ,进行类型检查 根据抽象语法树,生成中间代码;
XML 文件解析;
Spring 中的 BeanDefinitionVisitor;