3分钟看懂设计模式01:策略模式

news2024/9/21 0:25:44

一、什么是策略模式

定义一些列算法类,将每一个算法封装起来,并让它们可以互相替换。

策略模式让算法独立于使用它的客户而变化,是一种对象行为型模式。

以上是策略模式的一般定义,属于是课本内容。

在没有真正理解策略模式之前并不需要对此定义下过多功夫,读一遍直接进入下一章节。

二、为什么要用策略模式

我们应该知道,所谓的设计模式实际上是一种经过检验的、科学高效的、针对某种场景的最佳编程设计实践

所以要理解某一种设计模式,就必须知道我们什么时候可以用,用之前和用之后到底有什么区别。

练习:

假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。

一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂

比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

根据描述,折扣是根据以下的几个算法中的一个进行的:

算法一:对初级会员没有折扣。

算法二:对中级会员提供10%的促销折扣。

算法三:对高级会员提供20%的促销折扣。

给出一本图书,如300元,若是高级会员,则输出价格为240元。

pexels-karolina-grabowska-5650026.jpg

针对以上场景,大多数的我们写的代码就是使用的if…else…。

我们先提前揭晓,这种场景下就是我们使用策略模式的最佳时机。

那在我们尝试使用策略模式改进代码之前,我们必须要问:

if…else到底有什么问题?

传统实现方式

public Double calculationPrice(String type, Double originalPrice, int n) {

    //中级会员计费
    if (type.equals("intermediateMember")) {
        return originalPrice * n - originalPrice * 0.1;
    }
    //高级会员计费
    if (type.equals("advancePrimaryMember")) {
        return originalPrice * n - originalPrice * 0.2;
    }
    //普通会员计费
    return originalPrice;
}

这种编码方式到底差在哪?

大佬告诉我们说:维护性差。

什么叫维护性差?

就是下次你想加个超级黄金vip会员,以及各种后续会员种类,你就要不断往里加if…else…,这就违反了开闭原则

这里又有另外两个问题:

什么是开闭原则?我凭啥要遵守开闭原则?

什么是开闭原则?

开闭原则比较好记忆,顾名思义:

对扩展开放,对修改关闭。

大意就是你想改东西,不要改原代码,而是进行扩展代码。

为什么要遵守开闭原则(以及各种乱七八糟的原则)?

简单直接一点就是,这些原则都是巨佬们总结出来的,你如果不懂,你就直接选择相信就好了。

解释一下就是:

系统随着开发的不断进展,需求不断增多,代码越来越长,如果没有合理框架的制约那就只能沦为一个扩展难、维护难的屎山。

所以我们遵守开闭原则就是说需要一个科学合理的框架规范我们的系统熵增,在不修改原代码的基础上让系统拥有灵活性和稳定性。

一句话,上面的代码直接修改原代码,时间久了系统只会沦为屎山。

那怎么使用策略模式改造,而遵守开闭原则呢?

策略模式如何实现

Strategy(抽象策略类)

它为所支持的算法声明抽象方法,是所有策略类的父类。它可以使抽象类或者具体类,也可以是接口。

public interface MemberStrategy {
    // 一个计算价格的抽象方法
    //price商品的价格 n商品的个数
    public double calcPrice(double price, int n);
}

ConcreteStrategy(具体策略类)

它实现了上面抽象策略类的抽象方法。

在实际运行中,这个具体的策略类将会代替在**环境类(Context)**中定义的抽象策略类对象最终执行不同的实现逻辑。

可以看到下面的代码中,三种不同的策略类实现了同一个抽象策略类,每种策略对应一种实现,分别应对一个业务处理方式。

// 普通会员——不打折
public class PrimaryMemberStrategy implements MemberStrategy { // 实现策略
    @Override
    public double calcPrice(double price, int n) {
        return price * n;
    }
}

// 中级会员 打百分之10的折扣
public class IntermediateMemberStrategy implements MemberStrategy{
    @Override
    public double calcPrice(double price, int n) {
        double money = (price * n) - price * n * 0.1;
        return money;
    }
}

// 高级会员类 20%折扣
public class AdvanceMemberStrategy implements MemberStrategy{
    @Override
    public double calcPrice(double price, int n) {
        double money = price * n - price * n * 0.2;
        return money;
    }
}

Context(环境类)

这个对我来说一开始很难理解。

主要是不能理解 Context 这个词在这里的意思,再加上网上一大堆直接翻译为“上下文”的文章博客,我直接吐了:

我不理解Context的意思,难道就能理解“上下文”的意思?

还有类似的:事务又是什么东西?

所以我直接不管这个Context是什么东西,直接看代码。

首先他是一个类,我们看这个类里有什么。

一个成员变量memberStrategy

一个构造方法

一个计算价格的方法,内容返回memberStrategy的calcPrice方法

往下看。

/**
 * 负责和具体的策略类交互
 * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
 */

// 上下文类/环境类
public class MemberContext {
    // 用户折扣策略接口
    private MemberStrategy memberStrategy;

    // 注入构造方法
    public MemberContext(MemberStrategy memberStrategy) {
        this.memberStrategy = memberStrategy;
    }

    // 计算价格
    public double qoutePrice(double goodsPrice, int n){
        // 通过接口变量调用对应的具体策略
        return memberStrategy.calcPrice(goodsPrice, n);
    }

}


接下来看测试类中 Context 类的使用是什么样子的。

    // 测试类
    public class Application {
        public static void main(String[] args) {

            // 具体行为策略
            MemberStrategy primaryMemberStrategy = new PrimaryMemberStrategy(); // 接口回调(向上转型)
            MemberStrategy intermediateMemberStrategy = new IntermediateMemberStrategy();
            MemberStrategy advanceMemberStrategy = new AdvanceMemberStrategy();

            // 用户选择不同策略
            MemberContext primaryContext = new MemberContext(primaryMemberStrategy);
            MemberContext intermediateContext = new MemberContext(intermediateMemberStrategy);
            MemberContext advanceContext = new MemberContext(advanceMemberStrategy);

            //计算一本300块钱的书
            System.out.println("普通会员的价格:"+ primaryContext.qoutePrice(300,1));// 普通会员:300
            System.out.println("中级会员的价格:"+ intermediateContext.qoutePrice(300,1));// 中级会员 270
            System.out.println("高级会员的价格:"+ advanceContext.qoutePrice(300,1));// 高级会员240
        }
    }

发现了什么?

Context都是被new出来的,new的时候传入的Strategy实现类全部不一样,你传的不一样,将来context.calcPrice()执行的逻辑就不一样。

懂了没有?

什么是上下文?

什么是Context?

就是随机应变,像变色龙一样随着不同的环境变化而自由变化。

开发者根据“上下文”不同的业务需求往Context里面放置不同的Strategy。

这就是Context上下文的意思。

这里的Strategy可以你自己new,你也可以把它放在配置类里面配置,然后在代码中读取,这样更加灵活方便。

三、使用策略模式的场景总结

那我们知道了策略模式怎么实现,也就是已经有了一把锤子在手上了,那什么时候用这把锤子呢?

1. 系统中需要动态地在几种算法中选择一种。

2. 一个对象有很多的行为,如果不用策略模式就只能用一大堆的if…else…来实现。

3. 不希望客户端知道复杂的、与算法相关的数据结构。在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

然后我们用一些实际的例子来理解策略模式的大概的使用场景:

1. 支付方式选择: 假设平台支持多种支付方式,比如微信、支付宝、银行卡等。

2. 数据渲染方式: 如果你有一个应用程序,它可以以多种格式输出数据,比如XML、JSON或CSV。

3. 导航策略: 导航应用多种路径计算方法,如最快路线、最短路线、避开收费路线等。

4. 压缩数据: 根据不同的情况(比如压缩率、速度等)使用不同的压缩算法(如ZIP、RAR、7z等)。

四、策略模式有什么好处

1. 完美支持了开闭原则。

2. 通过抽象算法和继承实现,避免了大量重复代码。

3. 避免了多重选择语句(硬编码,不易维护)。


往期推荐:

● 师爷,翻译翻译什么叫AOP

● 翻译,师爷师爷什么叫事务

● 纪念JDBC

● SpringBoot实现动态数据源配置‍

● 聚簇索引、回表与覆盖索引

● Java锁到底是个什么东西

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1464853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity3d Shader篇(九)— 世界空间法线纹理映射

文章目录 前言一、什么是世界空间法线纹理映射?1. 世界空间法线纹理映射工作原理2. 什么是世界空间?3. 切线空间法线纹理映射和世界空间法线纹理映射对比世界空间法线纹理映射:优点:缺点: 切线空间法线纹理映射&#x…

专145+总420+哈尔滨工业大学803信号与系统和数字逻辑电路考研经验哈工大电子信息与通信,真题,大纲,参考书。

自从高考失利没有考入哈工大,一直带着遗憾,今年初试专业课803信号与系统和数字逻辑电路145,总分420顺利圆满哈工大,了却了一块心病,回看这一年的复习起起落落,心中的那块初心,让我坚持到了上岸&…

springmvc+ssm+springboot房屋中介服务平台的设计与实现 i174z

本论文拟采用计算机技术设计并开发的房屋中介服务平台,主要是为用户提供服务。使得用户可以在系统上查看房屋出租、房屋出售、房屋求购、房屋求租,管理员对信息进行统一管理,与此同时可以筛选出符合的信息,给笔者提供更符合实际的…

外汇天眼:外汇交易不可不知的8大风险!

现在外汇交易中的风险主要有哪些? 外汇作为一种投资方式肯定有风险,我们要想的是尽量规避风险。 今天就给大家介绍一下现在外汇交易中的风险主要有哪些? 一、高杠杆风险 由于外汇保证金交易采用的杠杆比例,放大了损失的额度&…

【前端素材】推荐优质后台管理系统APP Zina平台模板(附源码)

一、需求分析 当我们从多个层次来详细分析后台管理系统时,可以将其功能和定义进一步细分,以便更好地理解其在不同方面的作用和实际运作。 1. 功能层次 a. 用户管理功能: 用户注册和登录:管理用户账户的注册和登录过程。权限管…

在Win11上部署Stable Diffusion WebUI Forge

Stable Diffusion WebUI Forge 是 Stable Diffusion WebUI(基于 Gradio)之上的平台,可简化开发、优化资源管理并加快推理速度。“Forge”这个名字的灵感来自“Minecraft Forge”。这个项目旨在成为SD WebUI的Forge。 与原始 WebUI&#xff0…

【数据结构】链式队列

链式队列实现&#xff1a; 1.创建一个空队列 2.尾插法入队 3.头删法出队 4.遍历队列 一、main函数 #include <stdio.h> #include "./3.linkqueue.h" int main(int…

漫漫数学之旅030

文章目录 经典格言数学习题古今评注名人小传 - 柏拉图 经典格言 不知道正方形对角线与它的边不可通约的人&#xff0c;根本不配人这个称号。——柏拉图&#xff08;Plato&#xff09; 好家伙&#xff0c;想象一下柏拉图老兄正站在古希腊的广场上&#xff0c;手里挥舞着一根画着…

流程图:理解、创建与优化的视觉工具

流程图&#xff1a;理解、创建与优化的视觉工具 引言 在日常生活和工作中&#xff0c;我们经常遇到需要描述一系列步骤或过程的情况。这些步骤可能是制作一杯咖啡、完成一个项目&#xff0c;或者是解决一个复杂的数学问题。流程图&#xff0c;作为一种强大的视觉工具&#xf…

自然语言处理(NLP)—— 神经网络自然语言处理(2)实际应用

本篇文章的第一部分是关于探索词嵌入&#xff08;word embedding&#xff09;向量空间。词嵌入是一种语言模型和文本表示技术&#xff0c;其中单词或短语从词汇表被映射到向量的高维空间中。通过这种方式&#xff0c;可以通过计算向量之间的距离来捕捉单词之间的语义关系。 1.…

PC8260小封装COT控制模式同步降压器18V/6A输出电流只需极少外围元器

描述 PC8260是一个高效率的600kHz&#xff0c;恒定导通时间&#xff08;COT&#xff09;控制模式同步降压DC-DC转换器提供高达6A的电流。PC8260集成主开关和极低同步开关RDS&#xff08;ON&#xff09;以将传导损耗降至最低。低输出电压纹波和小型外部电感器电容器尺寸通过600…

【EndNote20】Endnote20和word的一些操作

文章目录 前言一、如何导入参考文献到EndNote201.1.在谷歌学术或知网上下载文献1.2.将下载好的文件导入EndNote20(可批量导入)1.3.书籍如何导入 二、Word中加入参考文献 前言 做毕设时学习了EndNote20的一些使用方法&#xff0c;并在此慢慢做汇总。 一、如何导入参考文献到End…

快速构建 Debezium MySQL Example 数据库

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

力扣226 翻转二叉树 Java版本

文章目录 题目描述解题思路代码 题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root…

JavaSE多线程线程池

文章目录 1. 多线程入门1.1 多线程相关概念1.2 什么是多线程1.3 多线程的创建方式1.3.1 继承 Thread 的方式1.3.2 实现 Runnable 接口的方式1.3.3 实现 Callable 接口的方式1.3.4 Thread 类中常用方法1.3.5 sleep() 方法 和 wait() 方法区别&#xff1a; 2. 线程安全2.1 线程安…

苍穹外卖Day02——总结2

前期文章 文章标题地址苍穹外卖Day01——总结1https://blog.csdn.net/qq_43751200/article/details/135466359?spm1001.2014.3001.5501苍穹外卖Day01——解决总结1中存在的问题https://lushimeng.blog.csdn.net/article/details/135473412 总结2 前期文章1. 新增员工模块1.1 …

Tomcat 学习之 Filter 过滤器

目录 1 Filter 介绍 2 Filter 的生命周期 3 Filter 和 FilterChain 4 Filter 拦截过程 5 FilterConfig 6 Filter 使用 1 Filter 介绍 在 Tomcat 中&#xff0c;Filter 是一种用于拦截请求和过滤响应的组件&#xff0c;可以在请求到达 Servlet 之前或响应离开 Servlet 之后…

宝塔面板mysql使用root账户远程登录

今日在弄数据库备份&#xff0c;我们两台服务器&#xff0c;一台测试环境一个正式环境&#xff1b;使用linux宝塔面板&#xff0c;数据库都是服务器本地mysql&#xff0c;打算在测试服务器添加远程数据库备份正式环境的数据库&#xff0c;需要注意的是添加远程服务器后必须点一…

QEMU源码全解析 —— virtio(22)

接前一篇文章&#xff1a;QEMU源码全解析 —— virtio&#xff08;21&#xff09; 前几回讲解了virtio驱动的加载。本回开始讲解virtio驱动的初始化。 在讲解virtio驱动的初始化之前&#xff0c;先要介绍virtio配置的函数集合变量virtio_pci_config_ops。实际上前文书也有提到…

开源博客项目Blog .NET Core源码学习(9:Autofac使用浅析)

开源博客项目Blog使用Autofac注册并管理组件和服务&#xff0c;Autofac是面向.net 的开源IOC容器&#xff0c;支持通过接口、实例、程序集等方式注册组件和服务&#xff0c;同时支持属性注入、方法注入等注入方式。本文学习并记录Blog项目中Autofac的使用方式。   整个Blog解…