【设计模式】2.策略模式

news2024/11/26 1:44:39

前言

代码例子是来大话设计模式,本文主要是根据个人的理解,对书中的内容做学习笔记。如果个人理解的有问题,请各位大佬指正🙏。

基础遗忘了可以复习一下:
面向对象Java基础
简单了解UML类图

1、业务背景

商场收银软件

2、UML与代码

2.1 简单工厂实现

先复习,并看下怎么使用简单工厂实现
在这里插入图片描述

// 收费抽象类
public abstract class CashSuper {
	// 收取费用的抽象方法,参数为单价和数量
	public abstract double acceptCash(double price, int num);
}
// 正常收费
public class CashNormal extends CashSuper {
	// 原价返回
	public double acceptCash(double price, int num){
		return price * num;
	}
}
// 打折收费
public class CashRebate extends CashSuper {
	// 定义折扣变量
	private double moneyRebate = 1d;
	// 初始化时必须输入折扣率。八折就输入0.8
	public CashRebate(double moneyRebate){
		this.moneyRebate = moneyRebate;
	}
	// 计算收费时需要在原价基础上乘以折扣率
	public double acceptCash(double price, int num){
		return price * num * this.moneyRebate;
	}
}
// 返利收费
public class CashReturn extends CashSuper {
	private double moneyCondition = 0d; // 返利条件
	private double moneyReturn = 0d; // 返利值
	// 初始化时必须输入返利条件和返利值
	// 如:“满300返100”,就是moneyCondition=300,moneyReturn=100
	public CashReturn(double moneyRebate){
		this.moneyCondition = moneyCondition;
		this.moneyReturn = moneyReturn;
	}
	// 计算收费时,当达到返利条件,就原价减去返利值
	public double acceptCash(double price, int num){
		double result = price * num;
		if	(moneyCondition>0 && result >= moneyCondition)
			result = result - Math.floor(result / moneyCondition) * moneyReturn;
		return result;
	}
}

扩展:

Math.ceil()的理解 为向上取整(比其本身大的数取值) 取整的类型为double类型的整数 其小数部分变为0。
Math.floor()的理解 为向下取整(比其本身小的数取值) 取整的类型也是为double类型的整数其小数部分同样变为0.

// 收费工厂(收费对象生成工厂)
public class CashFactory {
	
	public static CashSuper createCashAccept(int cashType){
		CashSuper cs = null;
		switch (cashType) {
			case 1:
				oper = new CashNormal();
				break;
			case 2:
				oper = new CashRebate(0.8d);
				break;
			case 3:
				oper = new CashRebate(0.7d);
				break;
			case 4:
				oper = new CashReturn(300d,100d);
				break;
		}
		return cs;
	}
}

客户端程序主要部分:

	double price = 0d; 		// 商品单价
	int num = 0; 			// 商品购买数量
	double totalPrice = 0d; // 当前商品合计费用
	double total = 0d; 		// 总计所有商品费用
	
	...
	
	// 简单工厂模式根据discount的数字选择合适的收费类 生成实例
	CashSuper csuper = CashFactory.createCashAccept(discount);
	// 通过多态,可以根据不同的收费策略计算的到收费结果
	totalPrice = csuper.acceptCash(price,num);
	total = total + totalPrice;
  • 问题:
    加入新的促销手段,是可以使用简单工厂模式来解决,但它只解决对象创建的问题,而且由于工厂本身包扩所有的收费方式,如果是经常性的变动,每次维护或扩展都需要改动工厂,导致代码得重新编译部署,这该怎么办?

2.2 策略模式

策略模式:它定义了算法家族,分别封装,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的用户。

对于如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错,但算法本身只是一种策略,最重要的是这些算法是随时都可能相互替换的,这就是变化点,而封装变化点是我们面向对象的重要思维。

在这里插入图片描述

Strategy类,定义所有支持的算法的公共接口

public abstract class Strategy {
	public abstract void algorithmInterface();
}

ConcreteStrategy类,封装了具体的算法或行为,继承Strategy:

// A B C都一样
public class ConcreteStrategyA extends Strategy{
	public void algorithmInterface(){
		System.out.println("算法A实现");
	}
}

✨Context类,用一个ConcreteStrategy对象来配置,维护一个对Strategy对象的引用。【策略模式的核心】

public class Context {
	Strategy  strategy;
	// 初始化时,传入具体的策略对象【这里就是传入ConcreteStrategy对象,列入ConcreteStrategyA】
	public Context(Strategy strategy){
		this.strategy = strategy;
	} 
	// 上下文接口
	public void contextInterface(){
		// 根据具体的策略对象,调用其算法的方法
		strategy.algorithmInterface();
	}
}

对UML在代码中聚合的个人理解:
在这里插入图片描述
客户端代码

	Context context;
	
	// 由于实例化不同的策略,所以最终在调用context.contextInterface()时,
	// 获得的结果就不尽相同
	context = new Context(new ConcreteStrategyA()); // 子类 - 具体算法(策略)
	context.contextInterface();

2.3 简单工厂模式 和 策略模式 的区别✨✨

  1. 简单工厂模式:用Factory工厂类创建对象(策略,具体算法)
  2. 策略模式:用Context管理对象(策略,具体算法),不负责创建对象
    策略模式最主要的作用:相比Factory,Context不会因为同一个接口contextInterface()添加新的策略(算法,例如ConcreteStrategyD())而需要改动Context原本的代码。

【这里就根据上述例子理解,管理是Context获取抽象类(Strategy,对应客户端代码的new Context(new ConcreteStrategyA())),然后调用方法context.contextInterface()操作对象的方法】

2.4 策略模式实现✨

将商场收银软件使用策略模式实现,先看UML的变化。
在这里插入图片描述
由上图,我们可以知道,抽象类部分没有改变,这里就不做代码展示,遗忘的可以看2.1节的代码,这里就展示 context类 和 客户端代码 具体怎么写。

Context类

public class CashContext {

	// 声明Strategy类对象 【UML中的抽象类 收费】
	private CashSuper cs;
	
	// 通过构造方法,传入具体的收费策略【Strategy】
	public Context(CashSuper csuper){
		// 传入的策略csuper交给CashContext用声明的cs具体管理
		this.cs = csuper;
	} 
	
	// 如果抽象方法有多个,这种管理方法也可以写多个【注意:每个子类都必须实现】
	// 管理cs对象统一的抽象方法【cs是图指的 被管理对象】
	public double getResult(double price,int num){
		// 根据传入不同的收费策略,获得具体实现的返回结果
		return this.cs.acceptCash(price,num);
	}
}

客户端主要代码

	CashContext cc = null;
	// 根据用户输入,将对应的策略对象作为参数参入CashContext对象中
	switch(discount){
		case 1:
			cc = new CashContext(new CashNormal());
			break;
		case 2:
			cc = new CashContext(new CashRebate(0.8d));
			break;
		case 3:
		...
	}
	// 通过Context的getResult方法的调用,可以得到收取费用的结果
	// 让具体算法与客户进行隔离【即使是工厂模式也一样】
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;

  • 问题:
    这个样子写,就需要在客户端去判断用哪一个算法。
    【客户端代码相当于界面逻辑部分,应该与业务逻辑分开,这里客户端代码中创建各种策略,算是业务逻辑和界面逻辑耦合在一起(个人理解)】

2.5 策略与简单工厂结合✨

为了让业务逻辑和界面逻辑解耦,这里让策略模式与简单工厂模式结合,将判断的代码转移。

public class CashContext {

	// 声明Strategy类对象 【UML中的抽象类 收费】
	private CashSuper cs;
	
	// 通过构造方法,传入具体的收费策略【Strategy】
	// public Context(CashSuper csuper){ 【原本的写法】
	public Context(int cashType){
		// 根据类型,new一个策略交给CashContext用声明的cs具体管理
		switch(cashType){
			case 1:
				this.cs = new CashContext(new CashNormal());
				break;
			case 2:
				this.cs = new CashContext(new CashRebate(0.8d));
				break;
			case 3:
			...
	} 
	
	// 如果抽象方法有多个,这种管理方法也可以写多个【注意:每个子类都必须实现】
	// 管理cs对象统一的抽象方法【cs是图指的 被管理对象】
	public double getResult(double price,int num){
		// 根据传入不同的收费策略,获得具体实现的返回结果
		return this.cs.acceptCash(price,num);
	}
}

改动的个人理解:
在这里插入图片描述
客户端代码:

	CashContext cc = new CashContext(discount);

	// 通过Context的getResult方法的调用,可以得到收取费用的结果
	// 让具体算法与客户进行隔离【即使是工厂模式也一样】
	totalPrices = cc.getResult(price,num);
	total = total + totalPrices;

根据上述内容可以发现,简单工厂模式并非只有建一个工厂类的做法

  • 疑惑1:
    难道这个策略与工厂结合后,还是工厂模式?【小白的我就先不纠结了】

下面对比一下 简单工厂模式 与 策略模式与简单工厂结合

	// 简单工厂模式
	CashSuper csuper = CashFactory.createCashAccept(dscount);
	totalPrices = csuper.acceptCash(price,num);

	// 策略模式与简单工厂结合
	CashContext cc = new CashContext(discount);
	totalPrice = cc.getResult(price,num)

在这里插入图片描述

  • 疑惑2:
    如果将客户端的判断代码转移到context里面,那context不需要改动的优势不就没有了吗
    【但在另一方面(如上图),又进行了另一方面的解耦】

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

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

相关文章

双色球彩票系统---(java实现)

双色球彩票系统:需求:投注号码由6个红色号码和1个蓝色球号码组成。红色球号码从1-33中选择,蓝色球号码从1-16当中选择 * 红 蓝 * 一等奖 6 1 * 二等奖 6 0 * 三等奖 5 1 * 四等奖 5 0 * 4 1 * 五等奖 4 0 * …

NPCon:AI模型技术与应用峰会北京站 (参会感受)

8月12日,我有幸参加了在北京皇家格兰云天大酒店举行的“AI模型技术与应用峰会”。 这次会议邀请了很多技术大咖,他们围绕: 六大论点 大模型涌现,如何部署训练架构与算力芯片 LLM 应用技术栈与Agent全景解析 视觉GPU推理服务部署 …

ubuntu部署haproxy

HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理. 1、更新系统报 通过在终端中运行以下命令,确保所有系统包都是最新的 sudo apt updatesudo apt upgrade 2、安装Haproxy sudo apt install haproxy 设置开机自动启动haproxy服务 sudo systemctl enable h…

.gitignore匹配规则

目录 1.直接一个名称2.斜杠 /3.符号 *4.问号 ?5.感叹号 !6.gitkeep 借鉴抖音账号: 渡一前端提薪课 1.直接一个名称 会忽略目录下的所有该名称文件和文件夹,无论嵌套多深。 2.斜杠 / 1.斜杠在开头(/dist):忽略和.gitig…

开发规范(一):Mysql篇

1. 流程 数据库表结构的修改需要相关人员和Leader一起评审,保证符合涉及规范。 不允许使用root账号,所有开发和测试应当分配指定账号,并授予最小数据库权限 2. 数据库与表规范 表命名规范 常规表表名以 t_开头,t 代表 table 的意思…

Kafka第三课

Flume 由三部分 Source Channel Sink 可以通过配置拦截器和Channel选择器,来实现对数据的分流, 可以通过对channel的2个存储容量的的设置,来实现对流速的控制 Kafka 同样由三大部分组成 生产者 服务器 消费者 生产者负责发送数据给服务器 服务器存储数据 消费者通过从服务器取…

Redis数据结构——压缩列表ziplist

定义 压缩列表ziplist是Redis中列表和哈希键的底层实现方式之一。 当一个列表只包含少量列表项,并且每个列表项要么是小整数值,要么是较短的字符串时,那么Redis就会使用压缩列表来作为列表的底层实现。 另外,当一个哈希表中只包含…

FPGA + WS2812采灯控制

文章目录 一、WS2812C-2020-V11、产品概述2、引出端排列及功能3、数据传输时间4、数据传输方法 二、使用WS2812C显示图片1、静态显示2、动态显示 一、WS2812C-2020-V1 1、产品概述 WS2812C-2020-V1是一个集控制电路与发光电路于一体的智能外控LED光源;其外型采用最…

Docker中Tomcat部署步骤

第一次访问没有东西。

2023牛客暑期多校训练营9 I.Non-Puzzle: Segment Pair(tag:差分)

文章目录 题目大意题解参考代码 题目大意 1 ≤ n , l i , r i ≤ 5 ∗ 1 0 5 1 \leq n,l_i,r_i \leq 5*10 ^5 1≤n,li​,ri​≤5∗105 题解 这题 l / r l/ r l/r 的数据在 5 1 0 5 5\times 10^5 5105 ,想到差分。 特殊的是它有两条线段,对于同一个点…

Photoshop窗口->排列菜单下进行匹配缩放/位置/旋转

首先,在Photoshop中打开4张以上图片,并选择“窗口”->“排列”->"四联": 将鼠标移动至其中一张图片中,按住“Z”键,拖动鼠标,调整图片缩放比例至60.55%, 再选择“窗口”->“…

一篇打通,pytest自动化测试框架详细,从0到1精通实战(二)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 读取文件 1、读取…

Python中使用隧道爬虫ip提升数据爬取效率

作为专业爬虫程序员,我们经常面临需要爬取大量数据的任务。然而,有些网站可能会对频繁的请求进行限制,这就需要我们使用隧道爬虫ip来绕过这些限制,提高数据爬取效率。本文将分享如何在Python中使用隧道爬虫ip实现API请求与响应的技…

Grounding DINO:根据文字提示检测任意目标

文章目录 1. 背景介绍2. 方法创新2.1 Feature Extraction and Enhancer2.2 Language-Guided Query Selection2.3 Cross-Modality Decoder2.4 Sub-Sentence Level Text Feature2.5 Loss Function3. 实验结果3.1 Zero-Shot Transfer of Grounding DINO3.2 Referring Object Detec…

多线程并发服务器

代码&#xff1a; #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 6666 //1024~49151 #define IP "192.168.122.130" //ifconfig查看本机IP #include <pthread.h> //…

urllib与数据解析

urllib爬取数据 import urllib.request as request# 定义url url "https://www.baidu.com" #模拟浏览器发起请求获取响应对象 response request.urlopen(url)""" read方法返回的是字节形式的二进制数据 二进制--》字符串 解码 decode( 编码的格式…

CSDN编程题-每日一练(2023-08-14)

CSDN编程题-每日一练&#xff08;2023-08-14&#xff09; 一、题目名称&#xff1a;小股炒股二、题目名称&#xff1a;王子闯闸门三、题目名称&#xff1a;圆小艺 一、题目名称&#xff1a;小股炒股 时间限制&#xff1a;1000ms内存限制&#xff1a;256M 题目描述&#xff1a; …

一文带你拿下基于Selenium+Python的web自动化测试框架(附框架源码+项目实战)

一、什么是Selenium&#xff1f; Selenium是一个基于浏览器的自动化测试工具&#xff0c;它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分&#xff1a;Selenium IDE、Selenium WebDriver 和Selenium Grid。 1.Selenium IDE&#xff1a;Fir…

Amazon EMR Hudi 性能调优——Clustering

随着数据体量的日益增长&#xff0c;人们对 Hudi 的查询性能也提出更多要求&#xff0c;除了 Parquet 存储格式本来的性能优势之外&#xff0c;还希望 Hudi 能够提供更多的性能优化的技术途径&#xff0c;尤其当对 Hudi 表进行高并发的写入&#xff0c;产生了大量的小文件之后&…

CentOS查看磁盘IO

CentOS查看磁盘IO 使用iotop命令&#xff08;top命令对应&#xff09; sudo iotop该命令将实时显示磁盘IO活动&#xff0c;按IO使用率排序&#xff0c;并显示每个进程的IO信息。 使用iostat命令&#xff1a; iostat -dx该命令将显示磁盘IO统计信息&#xff0c;包括每个设备…