设计模式的应用场景(19)--访问者模式

访问者模式

定义:封装一些施加于某种数据结构上的操作,一旦这些操作需要改变的话,接受这个操作的数据结构可以保持不变。

优点:使用访问者模式,对于原来的类层次增加新的操作只需要实现一个具体访问者角色,而不必改变整个类层次。每个具体的访问者角色都对应于一个相关操作。

缺点:不适合具体元素角色经常发生变化的情况。每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。

使用场合:当一个对象结构包括很多类对象,它们有不同的接口,而系统要求这些对象实施一些依赖于某具体类的操作时,就可以使用访问者模式。

小巩要设计超市收银系统,碰到了困难,有的按重量计价,有的按物件计价,还有其他计价方式。
这时候可以使用访问者模式。
首先设计抽象元素类,提供访问者需要的accept方法

public interface Goods {
    double accept(Visitor visitor);
}

具体元素类,包括猪肉类,,酒类,电视机类

public class Pig implements Goods {
    public double accountByUnit() {
        System.out.println("猪肉按斤计价,购买的数量为:" + getCount() + "斤,购买的单价为:" + getPrice() + ",总价为:" + getCount() * getPrice());
        return getCount() * getPrice();
    }
	public double accept(Visitor visitor) {
		return visitor.visit(this);
    }

    public float getCount(){ return count; }

    public void setCount(float count){ this.count = count; }

    public float getPrice(){ return price; }

    public void setPrice(float price){ this.price = price; }

    private float count;
    private float price;
}

public class Wine implements Goods {
    public double accountByBottle() {
        System.out.println("酒按瓶计价,购买的数量为:" + getCount() + "瓶,购买的单价为:" + getPrice() + ",总价为:" + getCount() * getPrice());
        return getCount() * getPrice();
    }

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

    public int getCount(){
            return count;
        }

    public void setCount(int count){
            this.count = count;
        }

    public float getPrice(){ return price; }

    public void setPrice(float price){ this.price = price; }

    private int count;
    private float price;
}

public class Television implements Goods {
    public double accountByPiece() {
        System.out.println("电视按台计价,购买的数量为:" + getCount() + "台,购买的单价为:" + getPrice() + ",总价为:" + getCount() * getPrice());
        return getCount() * getPrice();
    }
	public double accept(Visitor visitor) {
		return visitor.visit(this);
    }

    public int getCount(){
            return count;
        }

    public void setCount(int count){
            this.count = count;
        }

    public float getPrice(){ return price; }

    public void setPrice(float price){ this.price = price; }

    private int count;
    private float price;
}

需要一个购物车类收集要买的具体对象

import java.util.List;
import java.util.ArrayList;

public class ShoppingCart {
    public void add(Object object) {
        list.add(object);
    }

    public void remove(Object object) {
        list.remove(object);
    }

    public List getList() {
        return list;
    }

    private List list = new ArrayList();
}

下面是访问者接口和实现类

public interface Visitor {
    double visit(Wine wine);
    double visit(Pig pig);
    double visit(Television television);
}
public class VisitorImpl implements Visitor {
    public double visit(Wine wine) {
        return wine.accountByBottle();
    }
    public double visit(Pig pig) {
        return pig.accountByUnit();
    }
    public double visit(Television television) {
        return television.accountByPiece();
    }
}

设计收银机类可以调用具体物件供访问的收费方法

public class AccountMachine {
    private double amt;
    public void account(List list) {
		Visitor visitor = new VisitorImpl();
        for (int i = 0; i < list.size(); i++) {
            amt += ((Goods)list.get(i)).accept(visitor);
        }
	}
    public double getAmt() {
        return amt;
    }
}

客户端调用的代码

public class Client {
    public static void main(String[] argv) {
        Wine wine = new Wine();
        wine.setCount(10);
        wine.setPrice(20f);
        Pig pig = new Pig();
        pig.setCount(10f);
        pig.setPrice(15f);
        Television television = new Television();
        television.setCount(1);
        television.setPrice(2000f);
        ShoppingCart shoppingCart = new ShoppingCart();
		shoppingCart.add(wine);
        shoppingCart.add(pig);
        shoppingCart.add(television);
		AccountMachine accountMachine = new AccountMachine();
		accountMachine.account(shoppingCart.getList());
		System.out.println("本次购物车内所有物品的总价为:" + accountMachine.getAmt());
    }
}