设计模式学习(十九):访问者模式

时间:2022-11-12 16:09:09

作者:Grey

原文地址:

博客园:设计模式学习(十九):访问者模式

CSDN:设计模式学习(十九):访问者模式

访问者模式

访问者模式是一种行为型模式。

访问者模式在结构不变的情况下动态改变对于内部元素的动作。

举例说明:

假设我们需要构造一台电脑,有主板( 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

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料