趣解设计模式之《小王与他的Apple商店》

2023-09-22 13:16:40

〇、小故事

小王开了一个Apple商店,每天销售量都很不错,但是,近期却有一件事让他很苦恼,那就是针对不同的角色用户,商品的售价是各不同的

比如说,对于普通消费者来说,对于最新的Apple产品,都是原价销售的;

那么,对于学生消费群体来说,由于每年开学都会有高校折扣的政策,为了减少学生客户群体的购买压力,是在原价的基础上打8折销售的;

那么,对于企业大客户来说,很多互联网公司都会采购Apple电脑作为办公电脑,往往这种企业采购的方式购买电脑的数量会非常的大。所以,为了促进与企业的合作,是在原价的基础上打5折销售的;

随着后续购买用户角色类型的增多,就会造成一大堆的if-else判断逻辑,来返回不同角色对应的不同价格,这样无疑会使得代码维护性越来越差,那有什么更好的办法解决这个问题吗?当然有了,解决办法就是我们今天要介绍的设计模式——访问者模式

一、模式定义

访问者模式Visitor Pattern

表示一个作用于某个对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作

根据上面的小故事,我们可以看到小王的主要困扰就是针对不同的用户类型要有不同的折扣,这样随着用户类型的增多或者减少,我们都要频繁的修改电脑组件类(CPUMemory)里的if-else价格判断逻辑。那我们来思考一下,哪些是变化的?哪些是不变的呢?

变化】用户类型,如:普通用户学生用户企业采购用户……
不变】计算机组件类标准价格(此处我们不考虑促销活动的折扣或免减),如:CPUMemory……

既然变化的是用户类型,那么我们何不把计算机组件中判断金额的逻辑部分转移到用户类型中呢?这样计算机组件就永久稳定了。

有了这样的想法之后,计算机组件每个类都只需要包含标价就可以了。然后,针对不同的用户类型打不同的折扣,这部分逻辑,就可以抽离到计算机组件类中

二、模式类图

根据上面的描述,我们首先来创建计算机组件接口: ComputerComponent.java,以及两个计算机组件的实现类CPU.javaMemory.java。然后再创建计算机类Computer.java,用于将计算机的各个组件类组合起来。那么针对不用的用户类型,我们首先创建访问者接口Visitor.java,然后创建3个实现类,分别是普通用户访问者PersonVisitor.java学生用户访问者StudentVisitor.java企业采购访问者CompanyVisitor.java。具体类图请见如下所示:

三、代码实现

创建计算机组件类ComputerComponent.java

public interface ComputerComponent {
    int price(); // 售价
    String version(); // 硬件版本
    String desc(); // 描述
    void accept(Visitor visitor);
}

创建CPU类Cpu.java

public class Cpu implements ComputerComponent {
    public int price = 100; // 全国标准售价
    @Override
    public int price() {
        return 100;
    }
    @Override
    public String version() {
        return "v1";
    }
    @Override
    public String desc() {
        return "英特尔CPU";
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visitorCpu(this);
    }
}

创建内存类Memory.java

public class Memory implements ComputerComponent {
    public int price = 400; // 全国标准售价
    @Override
    public int price() {
        return price;
    }
    @Override
    public String version() {
        return "v4";
    }
    @Override
    public String desc() {
        return "金士顿内存";
    }
    @Override
    public void accept(Visitor visitor) {
        visitor.visitorMemory(this);
    }
}

创建访问者类Visitor.java

public interface Visitor {
    void visitorCpu(Cpu cpu);
    void visitorMemory(Memory memory);
    int price();
    String visiterName();
}

创建个人用户类(不打折)PersonVisitor.java

public class PersonVisitor implements Visitor {
    public int totalPrice; // 总售价
    @Override
    public void visitorCpu(Cpu cpu) {
        totalPrice += cpu.price();
    }
    @Override
    public void visitorMemory(Memory memory) {
        totalPrice += memory.price();
    }
    @Override
    public int price() {
        return totalPrice;
    }
    @Override
    public String visiterName() {
        return "个人用户";
    }
}

创建学生用户(校园计划,打8折)StudentVisitor.java

public class StudentVisitor implements Visitor {
    public int totalPrice; // 总售价
    @Override
    public void visitorCpu(Cpu cpu) {
        totalPrice += cpu.price() * 0.8;
    }
    @Override
    public void visitorMemory(Memory memory) {
        totalPrice += memory.price() * 0.9;
    }
    @Override
    public int price() {
        return totalPrice;
    }
    @Override
    public String visiterName() {
        return "学生用户";
    }
}

创建公司大客户(打5折)CompanyVisitor.java

public class CompanyVisitor implements Visitor {
    public int totalPrice; // 总售价
    @Override
    public void visitorCpu(Cpu cpu) {
        totalPrice += cpu.price() * 0.5;
    }
    @Override
    public void visitorMemory(Memory memory) {
        totalPrice += memory.price() * 0.4;
    }
    @Override
    public int price() {
        return totalPrice;
    }
    @Override
    public String visiterName() {
        return "公司大客户";
    }
}

创建计算机类,组合上面的组件Computer.java

public class Computer {
    private ComputerComponent memory;
    private ComputerComponent cpu;
    public Computer() {
        memory = new Memory();
        cpu = new Cpu();
    }
    
    /**
     * 攒机方法
     * @Visitor 买电脑的客户角色
     */
    public void buildComputer(Visitor visitor) {
        // 买cpu
        cpu.accept(visitor);
        // 买内存
        memory.accept(visitor);
    }
}

创建测试类VisitorTest.java

public class VisitorTest {
    public static void main(String[] args) {
        Computer computer = new Computer();
        Visitor personVisitor = new PersonVisitor();
        Visitor studentVisitor = new StudentVisitor();
        Visitor companyVisitor = new CompanyVisitor();
        
        computer.buildComputer(personVisitor);
        System.out.println(String.format("针对%s,每台电脑售价为:%s元", personVisitor.visiterName(), personVisitor.price()));
        
        computer.buildComputer(studentVisitor);
        System.out.println(String.format("针对%s,每台电脑售价为:%s元", studentVisitor.visiterName(), studentVisitor.price()));
        
        computer.buildComputer(companyVisitor);
        System.out.println(String.format("针对%s,每台电脑售价为:%s元", companyVisitor.visiterName(), companyVisitor.price()));
    }
}

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」

更多推荐

SpringCLoud——Docker的基本介绍

什么是Docker项目部署问题大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:依赖关系复杂,容易出现兼容性问题开发、测试、生产环境有差异。DockerDocker如何解决依赖的兼容问题的?将应用的LIbs(函数库)、Deps(依赖)、配置与应用一起打包将每个应用放到一个隔离容器去运行,避免互相打扰首先要了解

QT Day2

Second.h#ifndefSECOND_H#defineSECOND_H#include<QWidget>namespaceUi{classSecond;}classSecond:publicQWidget{Q_OBJECTpublicslots:voidjump_slot();//接收跳转信号的槽函数public

Matter 协议系列:发现

Commissionable发现Commissionable发现发生在投入使用(未绑定)之前,指的是发现和识别Commissionable节点的过程。有三种方法可以通过这些方法中的任何一种来广播Commissionable的节点:蓝牙低功耗(BLE)Wi-FiSoft-AP基于IP的网络上的DNS-SD,也称为现有的I

laravel框架 - 集合篇

LaravelEloquent通常返回一个集合作为结果,集合包含很多有用的、功能强大的方法。你可以很方便的对集合进行过滤、修改等操作。本次教程就一起来看一看集合的常用方法及功能。你可以使用助手函数collect将数组转化为集合。$data=collect([1,2,3]);1.增加$data=collect([1,2,

Linux下进度条程序实现以及自动化构建工具makefile的实现和对回车键的理解

在实现进度条之前我们要了解一些有关实现进度条的知识。例如回车和makefile的使用自动化构建工具:makefilemakefile实现的目的就是可以进行自动化的编译与自动化清理,即实现好了makefile之后,仅仅使用make指令就可以直接完成程序的编译过程。先看一看咱们实现makefile之后得到的效果截图:这里m

Linux Day16 多线程的一些常见问题

目录一、多线程+fork()问题一:多线程中某个线程调用fork(),子进程会有和父进程相同数量的线程吗?1.1.1不使用fork前,让线程函数和主程序打印其进程号结果:结论:1.1.2在主程序中加入fork结果:结论:1.1.3线程函数加入fork()结果:结论:综上所述:多线程程序fork后,子进程只启用一条执行路

C++ 4种智能指针的定义与使用——学习记录008

1.智能指针1.1作用智能管理动态分配的内存,自动释放程序员new出来的内存,从而避免内存泄漏。1.2原理动态分配的内存交给有生命周期的对象处理,在对象过期时,内存的释放交给对象来处理。1.3使用方法#include<memory>auto_ptr<类型>变量名称(new类型);auto_ptr<string>str(

搜索引擎排名因素有哪些具体的细节?

搜索引擎排名因素有很多,以下是一些常见的因素:关键词密度和位置:搜索引擎会考虑关键词在网页上的出现频率和位置。关键词密度指的是关键词在网页内容中出现的频率与整个文本的比例。关键词的位置也很重要,例如,如果关键词出现在页面的顶部或标题标签中,则更有可能被搜索引擎重视。页面标题和元标签:这是搜索引擎对网页进行排名的关键因素

Observability:检测 OpenTelemetry 的推荐指南

作者:BahubaliShettiOpenTelemetry(OTel)正在稳步获得广泛的行业采用。作为主要的云原生计算基金会(CNCF)项目之一,其提交数量与Kubernetes一样多,它正在获得主要ISV和云提供商的支持,为该框架提供支持。许多来自金融、保险、科技和其他行业的全球公司开始对OpenTelemetry

Java -【字符串,数组,哈希表】常用操作

一.字符串创建字符串:可以使用双引号或者String类的构造方法创建字符串。Stringstr1="HelloWorld";Stringstr2=newString("HelloWorld");连接字符串:可以使用加号或者String类的concat()方法连接字符串。Stringstr3=str1+str2;Stri

【Linux】常用工具(上)

Linux常用工具一、Linux软件包管理器yum1.软件包2.查看软件包3.安装/卸载软件4.yum其他指令的功能二、Linux编辑器-vim使用1.vim的基本概念2.vim的基本操作(1)光标移动(命令模式)(2)光标定位(命令模式)(3)复制粘贴撤销(命令模式)(4)其他操作(命令模式)(5)保存并退出(底行模

热文推荐