设计模式第19篇:访问者模式

一.访问者模式介绍

  定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

二.访问者模式代码用例

  此处以购物车为例来说明,比如不同的商品有不同的结算方式,如果将商品的结算逻辑方法放在商品对象内部,则商品参加打折活动时又需要更改商品类,并且商品种类时非常多的,此时程序将变得不可维护。访问者模式就是解决这个问题,将每个商品的结算逻辑抽取出来,放在访问者(Visitor)类中,商品类中只需有接收访问者的方法accept(Visitor visitor)即可。

  1.商品类接口(抽象类也行,此处用接口)

interface ItemElement {

         int accept(ShoppingCartVisitor visitor);
}    

  2.商品类实例Book、Fruit

class Book implements ItemElement {

    private int price;
    private String isbnNumber;
    
    public Book(int cost, String isbn){
        this.price=cost;
        this.isbnNumber=isbn;
    }
    
    public int getPrice() {
        return price;
    }

    public String getIsbnNumber() {
        return isbnNumber;
    }

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

}

class Fruit implements ItemElement {
    
    private int pricePerKg;
    private int weight;
    private String name;
    
    public Fruit(int priceKg, int wt, String nm){
        this.pricePerKg=priceKg;
        this.weight=wt;
        this.name = nm;
    }
    
    public int getPricePerKg() {
        return pricePerKg;
    }


    public int getWeight() {
        return weight;
    }

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

}

  3.访问者接口ShoppingCarVisitor

interface ShoppingCartVisitor {

    int visit(Book book);
    int visit(Fruit fruit);
}

  4.访问者接口实现ShoppingCarVisitorImpl

class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

    @Override
    public int visit(Book book) {
        int cost=0;
        //apply 5$ discount if book price is greater than 50
        if(book.getPrice() > 50){
            cost = book.getPrice()-5;
        }else cost = book.getPrice();
        System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
        return cost;
    }

    @Override
    public int visit(Fruit fruit) {
        int cost = fruit.getPricePerKg()*fruit.getWeight();
        System.out.println(fruit.getName() + " cost = "+cost);
        return cost;
    }

}

  5.测试

public class ShoppingCartClient {

    public static void main(String[] args) {
        ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
                new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
        
        int total = calculatePrice(items);
        System.out.println("Total Cost = "+total);
    }

    private static int calculatePrice(ItemElement[] items) {
        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int sum=0;
        for(ItemElement item : items){
            sum = sum + item.accept(visitor);
        }
        return sum;
    }

}

三.访问者模式使用场景及优缺点

场景:1.对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

   2.需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

优点:

   1.使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。

      2.添加新的操作或者说访问者会非常容易。

      3.将对各个元素的一组操作集中在一个访问者类当中。

      4.使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。

      5.可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。

缺点:

      1.增加新的元素会非常困难。

   2.实现起来比较复杂,会增加系统的复杂性。

   3.破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构。

适用性:

     1.数据结构稳定,作用于数据结构的操作经常变化的时候。

     2.当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。

     3.有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。

原文地址:https://www.cnblogs.com/quxiangxiangtiange/p/10305500.html