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 { // 实现策略@Overridepublic double calcPrice(double price, int n) {return price * n;}
}// 中级会员 打百分之10的折扣
public class IntermediateMemberStrategy implements MemberStrategy{@Overridepublic double calcPrice(double price, int n) {double money = (price * n) - price * n * 0.1;return money;}
}// 高级会员类 20%折扣
public class AdvanceMemberStrategy implements MemberStrategy{@Overridepublic 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));// 普通会员:300System.out.println("中级会员的价格:"+ intermediateContext.qoutePrice(300,1));// 中级会员 270System.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.mzph.cn/news/697115.shtml

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

相关文章

简单几步通过DD工具把云服务器系统Linux改为windows

简单几部通过DD安装其他系统,当服务器的web控制台没有我们要装的系统,就需要通过DD(Linux磁盘)工具来更改系统,(已知支持KVM系统) 本文如何简单的更换系统,不通过web控制台来更换&a…

mysql开启远程访问并开启3306端口

登陆mysql mysql -u root -p设置允许访问的地址 如果你想允许用户root从ip为192.168.1.123的主机连接到mysql服务器,并使用password密码登录。(根据情况自行替换) GRANT ALL PRIVILEGES ON *.* TO root192.168.1.123 IDENTIFIED BY passwo…

Python奇幻之旅(从入门到入狱高级篇)——面相对象进阶【中】

目录 2. 面向对象进阶 2.1. 成员 2.2. 变量 2.2. 方法 2.4. property属性 2.5. 成员修饰符 2.6. 对象嵌套 2.7. 特殊成员 引言 本篇内容主要是面向对象的进阶,讲解一些特殊的成员和方法,更加深入迭代器是如何通过对象实现的。 2. 面向对象进阶 …

Bellman-Ford(贝尔曼福特算法)

简介 贝尔曼-福特算法(Bellman-Ford Algorithm)是一种用于求解单源最短路径问题的算法,它可以处理带有负权边的图。 该算法的实现思路是通过不断迭代松弛操作来更新最短路径,直到找到最优解。 名词解释:1. 松弛操作&…

Qt 获取控件尺寸与实际不一致的问题

前提:界面ui获取桌面大小,用resize() 重新调整了界面尺寸 然后 我获取界面上某个控件大小时,发现与实际尺寸不一样。 最后发现: 获取控件大小的地方,必须在界面show()之后才可以,放之前不行。 注意; 经…

WPF 控件禁用时,显示悬浮提示

WPF 控件禁用时&#xff0c;显示悬浮提示 控件在禁用状态下&#xff0c;按钮是没有悬浮提示信息的&#xff0c;是事件触发的机制&#xff1b; 如果要禁用下也有悬浮提示&#xff0c;可以在控件外面加一层&#xff0c;例如&#xff1a; <Border Grid.Column"1" To…

Hive【内部表、外部表、临时表、分区表、分桶表】【总结】

目录 Hive的物种表结构特性 一、内部表 建表 使用场景 二、外部表 建表:关键词【EXTERNAL】 场景&#xff1a; 外部表与内部表可互相转换 三、临时表 建表 临时表横向对比​编辑 四、分区表 建表&#xff1a;关键字【PARTITIONED BY】 场景&#xff1a; 五、分桶表 …

CentOS 7.x 使用 RPM 包安装 Gitlab

官网&#xff1a;https://about.gitlab.com/ https://about.gitlab.cn/install/ 安装&#xff1a;https://gitlab.cn/install/ 博客&#xff1a;https://gitlab.cn/blog/ 文档&#xff1a;https://docs.gitlab.com/ https://about.gitlab.com/install/#centos-7 https://docs.g…

工作记录vue3 echarts地图等 监听浏览器等写法

子组件<template><div><div>【云端报警风险】</div><div ref"target" class"w-full h-full"></div></div> </template><script setup> import { ref, onMounted,watch } from vue; import * as ech…

算能RISC-V通用云开发空间编译pytorch @openKylin留档

终于可以体验下risc-v了&#xff01; 操作系统是openKylin&#xff0c;算能的云空间 尝试编译安装pytorch 首先安装git apt install git 然后下载pytorch和算能cpu的库&#xff1a; git clone https://github.com/sophgo/cpuinfo.git git clone https://github.com/pytorc…

小米14 Ultra:未来科技的集大成者

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

opencv图像的本质

目的 OpenCV是一个跨平台的库&#xff0c;使用它我们可以开发实时的计算机视觉应用程序。 它主要集中在图像处理&#xff0c;视频采集和分析&#xff0c;包括人脸检测和物体检测等功能。 数字图像在计算机中是以矩阵形式存储的&#xff0c;矩阵中的每一个元素都描述一定的图像…

VSCode React JavaScript Snippets 插件的安装与使用指南

VSCode React JavaScript Snippets 插件的安装与使用指南 在开发 React 项目时&#xff0c;提高效率是每个开发者都追求的目标之一。VSCode React JavaScript Snippets 插件就是为了提升 React 开发效率而设计的&#xff0c;它为常用的 React 代码片段提供了快捷键&#xff0c;…

NXP实战笔记(六):S32K3xx基于RTD-SDK在S32DS上配置PWM发波

目录 1、概述 2、SDK配置 2.1、Port配置 2.2、Emios_Mcl_Ip 2.3、Emios_Pwm 2.4、代码示例 1、概述 针对S32K3xx芯片&#xff0c;产生PWM的硬件支持单元仅有两个&#xff0c;分别是eMiosx与Flexio. 生成PWM的顺序&#xff0c;按照单片机所用资源进行初始化执行如下 初始化…

去年面试的运维开发面试题二

VPN有哪些协议&#xff0c;不同协议之间有何区别&#xff1f; 2.内部组网通常使用哪些类型的网段&#xff0c;两个不同网段如何通信&#xff1f; 3.Linux中绝对路径&#xff0c;相对路径的区别 4. Linux如何添加磁盘&#xff0c;扩容系统文件&#xff1f; 5. Linux如何查看进程…

原型模式(Prototype Pattern) C++

上一节&#xff1a;建造者模式&#xff08;Builder Pattern&#xff09;C 文章目录 0.理论1.原型模式的核心组成&#xff1a;2.实现方法3.什么时候使用 1.实践步骤 1: 定义怪物原型步骤 2: 实现具体怪物原型步骤 3: 使用原型创建怪物 0.理论 原型模式&#xff08;Prototype P…

7-liunx服务器规范

目录 概况liunx日志liunx系统日志syslog函数openlog 可以改变syslog默认输出方式 &#xff0c;进一步结构化 用户信息进程间的关系会话ps命令查看进程关系 系统资源限制改变工作目录和根目录服务器程序后台话 概况 liunx服务器上有很多细节需要注意 &#xff0c;这些细节很重要…

服务网格Service Mesh和Istio

文章目录 服务网格&#xff08;Service Mesh&#xff09;市场上三种服务网格解决方案服务网格的特征流量管理安全性可观察性 Istio简介Istio提供了什么功能服务 &#xff1f;Istio 核心特性流量管理安全可观察性 平台支持 服务网格&#xff08;Service Mesh&#xff09; 服务网…

Eureka注册中心(黑马学习笔记)

Eureka注册中心 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址和端口&#xff1f; 有多个user-service实例地址&#xff0c…

六、行列式基本知识

目录 1、行列式的特性 2、行列式的计算方法: 2.1 通过行列式的定义去计算:对角法则。 2. 2 利用行列式的性质将行列式转化为上三角行列式: ①行列式的性质 : 性质一: 性质二: 性质三: 性质四:行列式之间的加法