访问者模式(Visitor)

371

访问者模式(Visitor)

Visitor:访问者模式是一种行为设计模式,表示一个作用于某对象结构中的各元素的操作;它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

/**
 * 访问者模式
 *
 * @author CAI
 * @time 2020/12/9
 */
public class VisitorPattern {

    public static void main(String[] args) {
        List<ItemElement> items = new ArrayList<>();
        items.add(new Book(68, "IK12dd"));
        items.add(new Book(32, "GH52oq"));
        items.add(new Fruit(15, 3, "Orange"));
        items.add(new Fruit(30, 2, "Mango"));

        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int total = 0;

        for (ItemElement item : items) {
            total += item.accept(visitor);
        }

        System.out.println("Total cost = " + total);
    }
}

/**
 * 购物车访问者接口
 */
interface ShoppingCartVisitor {

    int visit(Book book);

    int visit(Fruit fruit);
}

/**
 * 商品
 */
interface ItemElement {

    int accept(ShoppingCartVisitor visitor);
}

/**
 * 图书
 */
class Book implements ItemElement {

    /**
     * 价格
     */
    private final int price;

    /**
     * ISBN 码
     */
    private final String isbnCode;

    public Book(int cost, String isbnCode) {
        this.price = cost;
        this.isbnCode = isbnCode;
    }

    public int getPrice() {
        return price;
    }

    public String getIsbnCode() {
        return isbnCode;
    }

    @Override
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
}

/**
 * 水果
 */
class Fruit implements ItemElement {

    /**
     * 每千克价格
     */
    private final int pricePreKg;

    /**
     * 重量(kg)
     */
    private final int weigh;

    /**
     * 水果名
     */
    private final String name;

    public Fruit(int pricePreKg, int weigh, String name) {
        this.pricePreKg = pricePreKg;
        this.weigh = weigh;
        this.name = name;
    }

    public int getPricePreKg() {
        return pricePreKg;
    }

    public int getWeigh() {
        return weigh;
    }

    public String getName() {
        return name;
    }

    @Override
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
}

/**
 * 访问接口实现类
 */
class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

    @Override
    public int visit(Book book) {
        int cost;

        // 单价超过 50 价格减去 5
        if (book.getPrice() > 50) {
            cost = book.getPrice() - 5;

        } else {
            cost = book.getPrice();
        }

        System.out.println("Book ISBN :: " + book.getIsbnCode() + " cost = " + cost);
        return cost;
    }

    @Override
    public int visit(Fruit fruit) {
        int cost = fruit.getPricePreKg() * fruit.getWeigh();

        System.out.println(fruit.getName() + " cost = " + cost);
        return cost;
    }
}
  1. 访问者(ShoppingCartVisitor)接口:声明一系列以对象结构的具体元素为参数的访问者方法。
  2. 具体访问者(ShoppingCartVisitorImpl)类:不同的元素类的实现。
  3. 元素(ItemElement)接口:声明一个方法来接收访问者,该方法必须有一个参数被声明为访问者接口类型。
  4. 具体元素(Book等)类:必须实现接收方法,该方法的目的是根据当前元素类将其调用重定向到相应的访问者的方法。
  5. 客户端:通过访问者接口与具体的元素对象交互。

应用场景

  1. 需要对一个复杂对象结构中的所有元素执行某些操作,可使用访问者模式。
    • 访问者模式通过在访问者对象中为多个目标类提供相同操作的变体,使能够在属于不同类的一组对象上执行同一操作。
  2. 可使用访问者模式来清理辅助行为的业务逻辑。
    • 该模式会将所有非主要的行为抽取到一组访问者类中,使得程序的主要类能更专注于主要的工作。
  3. 当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时,可使用访问者模式。
    • 可将该行为抽取到单独的访问者类中,只需要实现接收相关类的对象作为参数的访问者方法,并将其他方法留空即可。

优缺点

  • 优点:
    • 可以引入在不同类对象上执行的新行为,且无需对这些类做出修改;满足开闭原则。
    • 可将同一行为的不同版本移到同一个类中,满足单一职责原则。
  • 缺点:
    • 每次在元素层次结构中添加或移除一个类时,都要更新所有的访问者。
    • 在访问者同某个元素进行交互时,它们可能没有访问元素私有成员变量和方法的必要权限。