小侃设计模式(廿二)-访问者模式

1.概述

访问者模式(Visitor Pattern)指的是在类的内部结构不变的情况下,不同的访问者访问这个对象都会呈现出不同的处理方式。它的主要作用时将数据结构与数据操作分离,将不同的算法与其所作用的对象进行分离。本文将详述访问者模式的原理及其使用方式。

2.原理及使用

2.1 原理

访问者UML类图如下所示:
在这里插入图片描述
类图中主要包含五个核心元素:抽象访问者接口(Visitor)、具体访问者类(ConcreteVisitorA、ConcreteVisitorB)、抽象元素接口(Element)、具体元素类(ConcreteElementA、ConcreteElementB)、对象结构类(ObjectStructure)。具体解释如下:

抽象访问者接口(Visitor):接口或抽象类,定义了对每个Element访问行为,逻辑上它内部的方法个数与元素的个数是一致的,正常情况下,Visitor内部元素的类型相对稳定,如果该接口需要经常变更,说明模式选择有问题,可能更适合策略模式或其他模式;
具体访问者类(ConcreteVisitorA、ConcreteVisitorB):实现抽象接口Visitor,内部实现访问者访问不同元素的处理逻辑;
抽象元素接口(Element):接口或抽象类,它定义了一个accept()方法,用于调用访问者本身,实现具体逻辑;
具体元素类(ConcreteElementA、ConcreteElementB):实现抽象元素(Element),accept()方法大多数直接调用Visitor,可增加具体的处理逻辑;
对象结构类(ObjectStructure):负责关联访问者和元素对象,存储元素对象,并提供访问元素的方法,一般会有accept()方法来解释访问者对象。

2.2 案例

现在有个超市,叫胖东西,顾客可以办理不同类型的会员卡(铂金会员、黄金会员、白银会员和普通会员),到了年底,不同类型的会员买年货(猪肉、牛奶等)会有不同的折扣(铂金会员5折,黄金会员7折,白银会员9折,普通会员不打折)。
根据访问者模式,画出UML类图如下:
在这里插入图片描述
代码如下:

public interface Goods {

    double accept(Member member);

    void display();

}

public class Milk implements Goods{

    private String name="天真蓝水牛奶";

    private double price;

    private int number;

    public double getPrice() {
        return price;
    }

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

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

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

    @Override
    public double accept(Member member) {
        return member.visit(this);
    }

    @Override
    public void display() {
        System.out.println(name + ",价格:" + price + ",数量:" + number);
    }
}
public class Pork implements Goods {

    private String name = "进口黑猪肉";

    private double price;

    private int number;

    public double getPrice() {
        return price;
    }

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

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Pork(double price, int number) {
        this.price = price;
        this.number = number;
    }

    @Override
    public double accept(Member member) {
        return member.visit(this);
    }

    @Override
    public void display() {
        System.out.println(name + ",价格:" + price + ",数量:" + number);
    }
}

public class ObjectStructure {

    private static List<Goods> goods = new ArrayList<>();

    //计算总价
    public double accept(Member member) {
        double sum = goods.stream().mapToDouble(goods1 -> goods1.accept(member)).sum();
        return sum;
    }

    public void add(Goods good) {
        goods.add(good);
    }

    public void remove(Goods good) {
        goods.remove(good);
    }

}

public interface Member {

    //铂金会员折扣价
    double visit(Pork pork);

    //黄金会员折扣价
    double visit(Milk milk);


}

public class Platinum implements Member {

    @Override
    public double visit(Pork pork) {
        return 0.6 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.6 * milk.getPrice() * milk.getNumber();
    }
}

public class Gold implements Member {
    @Override
    public double visit(Pork pork) {
        return 0.7 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.7 * milk.getPrice() * milk.getNumber();
    }
}

public class Silver implements Member{
    @Override
    public double visit(Pork pork) {
        return 0.9 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.9 * milk.getPrice() * milk.getNumber();
    }
}

public class Normal implements Member {

    @Override
    public double visit(Pork pork) {
        return pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return milk.getPrice() * milk.getNumber();
    }

}

public class Client {

    public static void main(String[] args) {
        //铂金会员
        Platinum platinum = new Platinum();
        //黄金会员
        Gold gold = new Gold();
        //白银
        Silver silver = new Silver();
        //普通
        Normal normal = new Normal();

        ObjectStructure objectStructure = new ObjectStructure();
        //买10箱牛奶
        Goods milk = new Milk(129.0, 10);
        //买10斤猪肉
        Goods pork = new Pork(108.0, 10);

        objectStructure.add(milk);
        objectStructure.add(pork);

        double accept = objectStructure.accept(platinum);
        System.out.println("铂金会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept);

        double accept1 = objectStructure.accept(gold);
        System.out.println("黄金会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept1);

        double accept2 = objectStructure.accept(silver);
        System.out.println("白银会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept2);

        double accept3 = objectStructure.accept(normal);
        System.out.println("普通会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept3);

    }
}


运行结果如下:

在这里插入图片描述

2.3 优点和缺点

2.3.1 优点

1.符合单一职责原则,让程序具有优秀的扩展性、灵活性;
2.访问者模式适用于数据结构相对稳定的系统,可以用来做过滤器与拦截器之类的功能。

2.3.2 缺点

1.违背迪米特法则,访问者关注了其它类的内部细节;
2.违背了依赖倒置原则,访问者访问的是具体元素,而不是抽象元素。

3.小结

1.访问者模式是设计模式中较为复杂的一种模式,主要适用于对象对应的类改变较少,同时需要对对象进行较多操作的场景;
2.在某种程度上,访问者模式与策略模式和状态模式有一定相似性,策略模式是抽象算法类,让子类去实现具体算法,根据条件进行算法替换;状态模式是将状态封装到对象中,将状态与行为结合;访问者则是将不同访问者对应的操作封装到类中,每个类中都有策略。

4.参考文献

1.《设计模式之禅》-秦小波著
2.《大话设计模式》-程杰著
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.https://www.runoob.com/design-pattern/observer-pattern.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值