CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / DESIGN_PATTERNS / P-120 · 设计模式

设计模式 026:访问者(Visitor)

访问者模式(visitor pattern):当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式。把”作用于对象结构上的操作”封装成访问者,新增操作时只需新增一个访问者实现,无需改动元素类。

意图

为一个对象结构(比如组合结构)增加新能力。

类图

  • Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
  • ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
  • ObjectStructure:对象结构,可以是组合结构,或者是一个集合

原文图片丢失,据文字重绘。

classDiagram
    class Element {
        <<interface>>
        +accept(Visitor)
    }
    class Customer {
        -String name
        -List~Order~ orders
        +accept(Visitor)
    }
    class Order {
        -String name
        -List~Item~ items
        +accept(Visitor)
    }
    class Item {
        -String name
        +accept(Visitor)
    }
    class CustomerGroup {
        -List~Customer~ customers
        +accept(Visitor)
        +addCustomer(Customer)
    }
    class Visitor {
        <<interface>>
        +visit(Customer)
        +visit(Order)
        +visit(Item)
    }
    class GeneralReport {
        -int customersNo
        -int ordersNo
        -int itemsNo
        +visit(Customer)
        +visit(Order)
        +visit(Item)
        +displayResults()
    }
    Element <|.. Customer
    Element <|.. Order
    Element <|.. Item
    Visitor <|.. GeneralReport
    CustomerGroup o-- Customer
    Customer o-- Order
    Order o-- Item
    CustomerGroup ..> Visitor
    Element ..> Visitor : accept

实现

元素接口和一组组成对象结构的元素:Customer(客户)、Order(订单)、Item(条目),每个元素的 accept() 把自己交给访问者,并让访问者继续访问子元素:

public interface Element {
    void accept(Visitor visitor);
}

public class Customer implements Element {

    private String name;
    private List<Order> orders = new ArrayList<>();

    Customer(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }

    void addOrder(Order order) {
        orders.add(order);
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (Order order : orders) {
            order.accept(visitor);
        }
    }
}

public class Order implements Element {

    private String name;
    private List<Item> items = new ArrayList();

    Order(String name) {
        this.name = name;
    }

    Order(String name, String itemName) {
        this.name = name;
        this.addItem(new Item(itemName));
    }

    String getName() {
        return name;
    }

    void addItem(Item item) {
        items.add(item);
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (Item item : items) {
            item.accept(visitor);
        }
    }
}

public class Item implements Element {

    private String name;

    Item(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

对象结构 CustomerGroup,遍历所有元素并让它们接受访问:

class CustomerGroup {

    private List<Customer> customers = new ArrayList<>();

    void accept(Visitor visitor) {
        for (Customer customer : customers) {
            customer.accept(visitor);
        }
    }

    void addCustomer(Customer customer) {
        customers.add(customer);
    }
}

访问者接口和具体访问者 GeneralReport,在遍历过程中累计统计结果:

public interface Visitor {
    void visit(Customer customer);
    void visit(Order order);
    void visit(Item item);
}

public class GeneralReport implements Visitor {

    private int customersNo;
    private int ordersNo;
    private int itemsNo;

    public void visit(Customer customer) {
        System.out.println(customer.getName());
        customersNo++;
    }

    public void visit(Order order) {
        System.out.println(order.getName());
        ordersNo++;
    }

    public void visit(Item item) {
        System.out.println(item.getName());
        itemsNo++;
    }

    public void displayResults() {
        System.out.println("Number of customers: " + customersNo);
        System.out.println("Number of orders: " + ordersNo);
        System.out.println("Number of items: " + itemsNo);
    }
}
public class Client {

    public static void main(String[] args) {
        Customer customer1 = new Customer("customer1");
        customer1.addOrder(new Order("order1", "item1"));
        customer1.addOrder(new Order("order2", "item1"));
        customer1.addOrder(new Order("order3", "item1"));
        Order order = new Order("order_a");
        order.addItem(new Item("item_a1"));
        order.addItem(new Item("item_a2"));
        order.addItem(new Item("item_a3"));
        Customer customer2 = new Customer("customer2");
        customer2.addOrder(order);
        CustomerGroup customers = new CustomerGroup();
        customers.addCustomer(customer1);
        customers.addCustomer(customer2);
        GeneralReport visitor = new GeneralReport();
        customers.accept(visitor);
        visitor.displayResults();
    }
}

输出:

customer1
order1
item1
order2
item1
order3
item1
customer2
order_a
item_a1
item_a2
item_a3
Number of customers: 2
Number of orders: 4
Number of items: 6

想增加另一种报表,只需要再实现一个 Visitor,元素类和对象结构都不用改动——这就是访问者模式为对象结构增加新能力的方式。

JDK 中的应用

  • javax.lang.model.element.Element 与 javax.lang.model.element.ElementVisitor
  • javax.lang.model.type.TypeMirror 与 javax.lang.model.type.TypeVisitor

系列导航

← Angular 22+ 教程 26:Animation 动画 目录 JHipster 开发 26:性能调优实战 →
← 返回文章列表