設計模式學習(十九):訪問者模式

設計模式學習(十九):訪問者模式

作者: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 圖如下

image

訪問者模式的應用

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:[email protected]">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 圖

程式碼

更多

設計模式學習專欄

參考資料