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

一、什么是策略模式

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

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

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

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

二、为什么要用策略模式

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

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

练习:

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

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

比如,本网站可能对所有的高级会员提供每本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锁到底是个什么东西

图片

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法——排序算法

目录 文章目录 前言 一.排序的基本概念 1.什么是就地排序 2.什么是内部排序和外部排序 3.什么是稳定排序 4.判定一个排序算法的是稳定的 二.插入排序算法 1.直接插入排序 1.1基本思想 1.2复杂度 1.3稳定性 1.4代码演示 2.折半插入排序 2.1基本思想 2.2性能 3.…

AIGC 实战:如何使用 Docker 在 Ollama 上离线运行大模型(LLM)

Ollama简介 Ollama 是一个开源平台,用于管理和运行各种大型语言模型 (LLM),例如 Llama 2、Mistral 和 Tinyllama。它提供命令行界面 (CLI) 用于安装、模型管理和交互。您可以使用 Ollama 根据您的需求下载、加载和运行不同的 LLM 模型。 Docker简介 D…

【Unity】Unity与安卓交互

问题描述 Unity和安卓手机进行交互,是我们开发游戏中最常见的场景。本教程将从一个简单的例子来演示一下。 本教程需要用到Android Studio2021.1.1 1.Android Studio新建一个工程 2.选择Empty Activity 然后点击Next 3.点击Finish完成创建 4.选择File-New-New Mo…

#LLM入门|Prompt#1.3_迭代优化_Iterative

有了任务想法后,可以先编写初版 Prompt,注意清晰明确并给模型充足思考时间。运行后检查结果,如果不理想,则分析 Prompt 不够清楚或思考时间不够等原因,做出改进,再次运行。如此循环多次,终将找到…

国际联合行动“克罗诺斯”成功打击LockBit勒索软件组织,基础设施遭查封

在一场旷日持久的执法行动中,美国联邦调查局(FBI)和其国际盟友终于成功打击了臭名昭著的网络犯罪团伙LockBit。通过名为“克罗诺斯”的联合执法行动,执法部门取得了阶段性的胜利,但与网络犯罪的斗争依然任重道远。 第一章 克罗诺斯行动的进展…

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…

【Python笔记-设计模式】适配器模式

一、说明 适配器模式是一种结构型模式&#xff0c;它使接口不兼容的对象能够相互合作 (一) 解决问题 主要解决接口不兼容问题 (二) 使用场景 当系统需要使用现有的类&#xff0c;但类的接口不符合需求时当需要一个统一的输出接口&#xff0c;但输入类型不可预知时当需要创…

使用openai-whisper实现语音转文字

使用openai-whisper实现语音转文字 1 安装依赖 1.1 Windows下安装ffmpeg FFmpeg是一套可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。 # ffmpeg官网 https://ffm…

React18源码: reconcliler启动过程

Reconcliler启动过程 Reconcliler启动过程实际就是React的启动过程位于react-dom包&#xff0c;衔接reconciler运作流程中的输入步骤.在调用入口函数之前&#xff0c;reactElement(<App/>) 和 DOM对象 div#root 之间没有关联&#xff0c;用图片表示如下&#xff1a; 在启…

多线程-day4

1>多线程完成文件拷贝 代码&#xff1a; #include<myhead.h>//参数结构体创建 typedef struct INFO {const char *srcfile;const char *destfile;int length; }Info;//定义获取文件长度的函数 int get_file_len(const char *srcfile,const char *destfile){int srcf…

C++秋招必知必会(常用排序算法)

参考引用 Hello 算法 常用排序算法 1. 选择排序 1.1 算法原理与流程 开启一个循环&#xff0c;每轮从未排序区间选择最小的元素&#xff0c;将其放到已排序区间的末尾设数组的长度为 n n n&#xff0c;选择排序的算法流程如下图 初始状态下&#xff0c;所有元素未排序&…

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

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

电脑亮度怎么调节?一文详解,亮度调整随心所欲!

在我们日常使用电脑的时候&#xff0c;屏幕亮度的合理调节是关乎舒适度和视觉体验的一项重要任务。无论是在白天面对明亮的阳光&#xff0c;还是在夜晚为了减少眼睛疲劳&#xff0c;灵活地调整电脑屏幕亮度&#xff0c;让其适应不同环境和个人需求&#xff0c;成为了提高工作效…

虚拟列表【vue】等高虚拟列表/非等高虚拟列表

文章目录 1、等高虚拟列表2、非等高虚拟列表 1、等高虚拟列表 参考文章1 参考文章2 <!-- eslint-disable vue/multi-word-component-names --> <template><divclass"waterfall-wrapper"ref"waterfallWrapperRef"scroll"handleScro…

vue里echarts的使用:画饼图和面积折线图

vue里echarts的使用,我们要先安装echarts,然后在main.js里引入: //命令安装echarts npm i echarts//main.js里引入挂载到原型上 import echarts from echarts Vue.prototype.$echarts = echarts最终我们实现的效果如下: 头部标题这里我们封装了一个全局公共组件common-he…

Spark: a little summary

转眼写spark一年半了&#xff0c;从之前写机器学习组件、做olap到后面做图计算&#xff0c;一直都是用的spark&#xff0c;惭愧的是没太看过里面的源码。这篇文章的目的是总结一下Spark里面比较重要的point&#xff0c;重点部分会稍微看一下源代码&#xff0c;因为spark是跟cli…

基于springboot+vue的靓车汽车销售网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Android中Transition过渡动画的简单使用

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 一、布局xml文件代码如下&#xff1a; <?xml version"1.0" encoding&quo…

【ctfshow—web】——信息搜集篇1(web1~20详解)

ctfshow—web题解 web1web2web3web4web5web6web7web8web9web10web11web12web13web14web15web16web17web18web19web20 web1 题目提示 开发注释未及时删除 那就找开发注释咯&#xff0c;可以用F12来查看&#xff0c;也可以CtrlU直接查看源代码呢 就拿到flag了 web2 题目提示 j…

H5移动端文件预览pdf

H5移动端文件预览pdf 需求&#xff1a;H5页面嵌入浙政钉&#xff0c;需要文件预览Pdf。 试用了多个插件&#xff0c;踩了很多坑&#xff0c;如果小伙伴有类似填坑经历&#xff0c;并成功解决&#xff0c;感谢留言指点&#xff01;&#xff01;&#xff01; 先讲最终方案&#x…