Java设计模式之--访问者模式(Visitor)

概念:

访问者模式(Visitor)行为设计模式。访问者模式被用在针对一组相同类型对象的操作。优点是,可以把针对此对象的操作逻辑转移到另外一个类上。用于数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。


案例:

本文将展示如何利用访问者模式去实现电商购物车系统,平台上的商品的销售活动(操作行为)可能经常改变,但商品本身基本数据却相对稳定,这里就可以应用访问者模式,将行为与数据分离开来,达到解耦的目的。下面的例子并对访问者模式进行改进,让增加数据与改变操作行为一样方便。

先来看类图:



先定义商品项接口:两个方法等实现 

public interface GoodsItem {
    public double accept(ShoppingCartVisitor visitor);

    public ShoppingCartVisitor getSelfVisitor();
}

定义购物接口:

public interface ShoppingCartVisitor {
    public double visitor(GoodsItem goodsItem);
}

定义商品类,实现GoodsItem 接口:

奶粉类:

public class Milk implements GoodsItem {

    private String brand;
    private double price;
    private int number;

    public int getNumber() {
        return number;
    }

    public Milk(String brand, double price, int number) {
        this.brand = brand;
        this.price = price;
        this.number = number;
    }

    public String getBrand() {
        return brand;
    }

    public double getPrice() {
        return price;
    }

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

    @Override
    public ShoppingCartVisitor getSelfVisitor() {
        return new MilkVisitor();
    }
}

樱桃:

public class Cherry implements GoodsItem {

    private double price;

    private int weight;

    public double getPrice() {
        return price;
    }

    public int getWeight() {
        return weight;
    }

    public Cherry(double price, int weight) {
        this.price = price;
        this.weight = weight;
    }

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

    @Override
    public ShoppingCartVisitor getSelfVisitor() {
        return new CherryVisitor();
    }
}

增加奶粉Visitor

public class MilkVisitor implements  ShoppingCartVisitor {
    @Override
    public double visitor(GoodsItem goodsItem) {
        Milk milk = (Milk)goodsItem;
        double cost = milk.getPrice()*milk.getNumber();
        System.out.println(String.format("%s 单盒价:%s,盒数%s 总价:%s",milk.getBrand(),milk.getPrice(),milk.getNumber(),cost));
        //奶粉满300减50
        if(cost>=300){
            cost-=50;
        }
        System.out.println(String.format("%s 今日满300减50,优惠后总价:%s",milk.getBrand(),cost));
        return cost;
    }
}

增加樱桃Visitor

public class CherryVisitor implements  ShoppingCartVisitor {
    @Override
    public double visitor(GoodsItem goodsItem) {
        Cherry cherry = (Cherry)goodsItem;
        double cost = cherry.getPrice()*cherry.getWeight();
        System.out.println(String.format("Cherry 单价:%s 重量:%s 总价:%s",cherry.getPrice(),cherry.getWeight(),cost));
        //进口樱桃 8折
        cost*=0.8;
        System.out.println(String.format("Cherry 今日8折,折后总价%s",cost));
        return cost;
    }
}

最后购物平台类:

public class ShopingClient {
    private List<GoodsItem> list;

    public ShopingClient(List<GoodsItem> list) {
        this.list = list;
    }

    public List<GoodsItem> getList() {
        return list;
    }

    public void setList(List<GoodsItem> list) {
        this.list = list;
    }

    public double perchase() {
        double costTotal = 0.0;
        for (GoodsItem goodsItem : list) {
            costTotal += goodsItem.accept(goodsItem.getSelfVisitor());
        }
        System.out.println(String.format("购物总价%s", costTotal));
        return costTotal;
    }

}

写代码测试:

public class WorkClass {
    public void test() {
        List<GoodsItem> list = new ArrayList<GoodsItem>();
        list.add(new Milk("某品牌奶粉",160.0,2));
        list.add(new Cherry(80.0,5));
        ShopingClient shopingClient = new ShopingClient(list);
        double totalcost=shopingClient.perchase();

    }
}

测试结果输出:

I/System.out: 某品牌奶粉 单盒价:160.0,盒数2 总价:320.0
I/System.out: 某品牌奶粉 今日满300减50,优惠后总价:270.0
I/System.out: Cherry 单价:80.0 重量:5 总价:400.0
I/System.out: Cherry 今日8折,折后总价320.0
I/System.out: 购物总价590.0


结语:

上面的例子,新增商品数据也非常方便。增加一个商品类和它的购物访问类就可以了。不用改到已有的类和接口。

如果项目需要为一个现有的类增加新功能,会考虑以下几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添加?3、如果类不允许修改现代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。这样修改功能不会影响到数据本身。


上面的例子,新增商品数据也非常方便。增加一个商品类和它的购物访问类就可以了。
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值