解锁Spring Boot中的设计模式—05.策略模式:探索【策略模式】的奥秘与应用实践!

news2025/6/28 3:43:59

1.策略者工厂模式(Map版本)

1.需求背景

假设有一个销售系统,需要根据不同的促销活动对商品进行打折或者其他形式的优惠。这些促销活动可以是针对不同商品类别的,比如男装、女装等。

2.需求实现
  1. 活动策略接口:定义了所有促销活动的公共接口,包括展示活动的方法。
  2. 具体策略活动:实现了活动策略接口的具体策略类,每个具体策略类代表一种促销活动,比如活动A和活动B。
  3. 连接策略的上下文:即环境角色,用于连接具体的促销活动和客户端。它持有一个策略对象,根据客户端的需求展示对应的活动。
  4. 策略工厂:提供了一种根据活动类型获取对应策略对象的方法,以便客户端根据需要选择促销活动。
3.策略者模式优点
  • 提供了对开闭原则的支持,可以灵活增加新的促销活动而无需修改现有代码。
  • 避免了使用多重条件语句,提高了代码的可读性和可维护性。
  • 可以方便地管理和组织相关的算法族,提高了代码的组织性和复用性。
4.策略者模式缺点
  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类,因此不适用于客户端不知道所有算法或行为的情况。
  • 可能会造成策略类的过多,每个具体策略类都会产生一个新类,增加了系统的复杂性。
5.应用场景
  • 当一个系统中存在多种相似但不同的算法或行为,并且需要动态切换时,可以考虑使用策略者模式。
  • 当需要在不修改现有代码的情况下灵活地增加新的算法或行为时,策略者模式也是一个不错的选择。

策略者模式在销售系统、支付系统、游戏开发等领域都有广泛的应用。

实现

1.1.具体策略活动
/**
 * 具体策略活动A
 */
public class StrategyA implements Strategy {
    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    @Override
    public void show() {
        logger.error("活动A,减999!");
    }
}



/**
 * 活动B
 */
public class StrategyB implements Strategy {
    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
    @Override
    public void show() {
        logger.error("活动B,减999999!");
    }
}

1.2.所有策略的公共接口
/**
 * 定义所有活动的公共接口
 */
public interface Strategy {
    void show();

}

1.3.连接策略的上下文
/**
 * 定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员
 */
public class ManStrategy {
    /**
     * 持有策略者角色的应用(传入啥执行啥)
     * SalesMan salesMan = new SalesMan(new StrategyA());
     * SalesMan salesManB = new SalesMan(new StrategyB());
     */
    private Strategy strategy;

    /**
     * 初始化时 将具体的策略者 赋值给当前策略者引用
     * @param strategy
     */
    public ManStrategy(Strategy strategy){
        this.strategy = strategy;
    }


    /**
     * 展示具体的策略
     */
    public void showInfoStrategy(){
        strategy.show();
    }
}

1.4.策略工厂
public class HandlerStrategyFactory {
    private static final Map<String, Strategy> map = new HashMap<>();
    static {
        map.put("男装",new StrategyA());
        map.put("女装",new StrategyB());
    }
    public static Strategy getStrategy(String type){
        return map.get(type);
    }
}
1.5.测试
public class StrategyDemo {
    public static void main(String[] args) {
        // 对象模式
        ManStrategy manStrategy = new ManStrategy(new StrategyB());
        manStrategy.showInfoStrategy();
        // --------------------------- map集合 --------------------------
        // 根据对应各key 找到对应的实现类 然后执行对应方法 MAP集合 方式
        Strategy strategy = HandlerStrategyFactory.getStrategy("女装");
        strategy.show();

    }
}

在这里插入图片描述

2.策略工厂模式(Spring版本)

1. 简介

策略工厂模式是一种行为型设计模式,用于灵活选择不同算法,而不必改变代码结构。

2. 核心思想
  • 定义抽象策略接口和具体策略类。
  • 实现策略工厂,根据需求选择合适的策略类。
3. 案例背景

电商系统需要根据不同促销活动对商品进行打折,促销策略基于活动季节、产品类型等选择。

4. 示例要点
  • 定义抽象策略接口:PromotionStrategy,包含applyDiscount()方法。
  • 创建具体促销策略类如SpringFestivalPromotionMemberDayPromotion
  • 实现策略工厂PromotionStrategyFactory,根据需求选择合适的促销策略。
5. 应用场景
  • 需要根据不同条件选择合适算法。
  • 系统需要在不同环境下灵活切换算法。

策略工厂模式提供了灵活性和可维护性,使系统更加健壮。

2.1.类图

在这里插入图片描述

3.具体实现

3.1.抽象策略接口

/**
 * 抽象策略接口
 */
public interface PromotionStrategy<R,T,U> {

	/**
	 * 应用折扣
	 * @param t 商品信息
	 * @param u 用户信息
	 * @return 折扣后的最终价格
	 */
	R applyDiscount(T t, U u);
}

3.2.策略具体实现类

3.2.1.春节促销策略实现类
package com.hrfan.java_se_base.pattern.strategy_pattern;

import com.hrfan.java_se_base.log.model.UserInfoToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;


/**
 * 春节促销 策略实现类
 */
@Service
public class SpringFestivalPromotion implements PromotionStrategy<BigDecimal, String, UserInfoToken> {
	private final Logger logger = LoggerFactory.getLogger(SpringFestivalPromotion.class);
	/**
	 * 春节折扣信息
	 * @param goodsName 商品信息
	 * @param userInfoToken 用户信息
	 * @return
	 */
	@Override
	public BigDecimal applyDiscount(String goodsName, UserInfoToken userInfoToken) {
		logger.error("============= {},春节大促销,原价100,现在6折 =============",goodsName);
		BigDecimal account = new BigDecimal("100");
		BigDecimal res = account.multiply(new BigDecimal("0.6")).setScale(2,BigDecimal.ROUND_HALF_UP);
		logger.error("============= 最终价格仅为:{},速来抢购! =============",res);
		return res;
	}
}

3.2.2.会员日策略实现类
package com.hrfan.java_se_base.pattern.strategy_pattern;

import com.hrfan.java_se_base.log.model.UserInfoToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;

/**
 * 会员日策略实现类
 */
@Service
public class MemberDayPromotion implements PromotionStrategy<BigDecimal,String, UserInfoToken> {

	private final Logger logger = LoggerFactory.getLogger(MemberDayPromotion.class);
	/**
	 * 会员日商品促销策略
	 * @param goodsName 商品信息
	 * @param userInfoToken 用户信息
	 * @return 最终优惠后的价格
	 */
	@Override
	public BigDecimal applyDiscount(String goodsName, UserInfoToken userInfoToken) {
		logger.error("============= {},会员日大促销,原价100,现在3折 =============",goodsName);
		BigDecimal account = new BigDecimal("100");
		BigDecimal res = account.multiply(new BigDecimal("0.3")).setScale(2,BigDecimal.ROUND_HALF_UP);
		logger.error("============= 最终价格仅为:{},速来抢购! =============",res);
		return res;
	}
}

3.3.创建策略工厂

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

/**
 * 策略工厂
 */
@Service
public class PromotionStrategyFactory {

    @Resource
    private ApplicationContext context;


    /**
     * 选择对应的策略
     */
    public PromotionStrategy createPaymentStrategy(String paymentMethod) {
        try {
            return context.getBean(paymentMethod, PromotionStrategy.class);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("未匹配到对应策略:" + paymentMethod);
        }
    }
}

4.测试

import com.hrfan.java_se_base.config.ResultObject;
import com.hrfan.java_se_base.log.model.UserInfoToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.math.BigDecimal;

/**
 * 策略模式测试方法
 */
@RestController
@RequestMapping("/v1/strategy/")
public class StrategyController {
	@Resource
	private PromotionStrategyFactory promotionStrategyFactory;

	@PostMapping("/getLastPrice")
	public ResultObject getLastPrice(@RequestParam("type") String type, UserInfoToken userInfoToken){
		ResultObject instance = ResultObject.createInstance(true);
		// 获取商品的最终价格 (根据传入的策略然后选择对应的实现类 该type就是对应实现类的名称)
		PromotionStrategy strategy = promotionStrategyFactory.createPaymentStrategy(type);
		BigDecimal res = (BigDecimal) strategy.applyDiscount("鸡蛋", userInfoToken);
		instance.setData(res);
		instance.setMessage("获取成功!");
		return instance;
	}
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

策略工厂模式 vs 普通策略模式:区别总结

1. 概念
  • 策略工厂模式: 策略工厂模式是策略模式的一种变体,它将策略的选择和创建交给了工厂类来处理,客户端通过工厂获取需要的具体策略对象。
  • 普通策略模式: 普通策略模式直接由客户端选择和创建具体的策略对象,客户端需要明确知道每个策略类的存在,并负责创建相应的对象。
2. 结构
  • 策略工厂模式: 策略工厂模式包含策略接口、具体策略类和策略工厂类。客户端通过策略工厂类获取具体的策略对象。
  • 普通策略模式: 普通策略模式包含策略接口和具体策略类。客户端直接选择和创建具体的策略对象。
3. 使用场景
  • 策略工厂模式: 适用于需要根据条件选择不同策略,并且需要将策略选择逻辑与客户端分离的场景。适用于策略类较多,客户端不需要直接了解每个策略类的情况。
  • 普通策略模式: 适用于策略较少,客户端可以直接了解并选择每个策略类的情况。
4. 灵活性
  • 策略工厂模式: 策略工厂模式更加灵活,可以动态地切换策略,不需要修改客户端代码。
  • 普通策略模式: 普通策略模式相对固定,客户端需要显式地选择并创建具体的策略对象。
5. 维护性
  • 策略工厂模式: 策略工厂模式使得系统更易于扩展和维护,添加新的策略只需修改工厂类。
  • 普通策略模式: 普通策略模式需要客户端直接了解每个策略类,维护起来相对困难。
6. 总结
  • 策略工厂模式: 提供了更高的灵活性和可维护性,将策略的选择和创建与客户端分离。
  • 普通策略模式: 简单直接,适用于策略较少,且客户端能够明确选择每个策略类的情况。

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

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

相关文章

【递归】【后续遍历】【迭代】【队列】Leetcode 101 对称二叉树

【递归】【后续遍历】Leetcode 101 对称二叉树 解法一&#xff1a; 递归&#xff1a;后序遍历 左右中解法二&#xff1a; 迭代法&#xff0c;用了单端队列 ---------------&#x1f388;&#x1f388;对称二叉树 题目链接&#x1f388;&#x1f388;------------------- 解法一…

cdn服务器是什么?cdn服务器怎么搭建

不少网友都在咨询CDN服务器是什么&#xff1f;CDN服务器是建立在网络上的内容分发网络。CDN服务器是一种新型的网络服务器构建方式&#xff0c;为了优化原有的网络构架服务&#xff0c;下面大家也一起交流下吧。 CDN服务器是什么&#xff1f; CDN即内容分发网络。所以CDN服务…

map的key重复问题

一种需要key重复的Map 实例结果 IdentityHashMap<>(); dentityHashMap 类&#xff08;存在于java.util包中&#xff09;是一个 基于HashTable的 Map 接口的实现&#xff0c;从Java 1.4版本开始就已经存在。 这个类不是一个通用的Map 实现。尽管这个类实现了Map 接口&…

SpringMVC速成(二)

文章目录 SpringMVC速成&#xff08;二&#xff09;1.SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.properti…

SSM框架,spring-aop的学习

代理模式 二十三种设计模式中的一种&#xff0c;属于结构型模式。它的作用就是通过提供一个代理类&#xff0c;让我们在调用目标方法的时候&#xff0c;不再是直接对目标方法进行调用&#xff0c;而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来…

【51单片机实验笔记】开关篇(二) 矩阵按键

目录 前言原理图分析矩阵按键扫描算法 软件实现1. 矩阵键盘检测2. 简易计算器实现 总结 前言 本节内容&#xff0c;我们学习一下矩阵按键&#xff0c;它是独立按键的阵列形式&#xff0c;常见的应用即键盘。 本节涉及到的封装源文件可在《模块功能封装汇总》中找到。 本节完…

MySQL性能分析1

1、查看执行频次 查看当前数据库的INSERT,UPDATE,DELETE,SELECT的访问频次&#xff0c;得到当前数据库是以插入&#xff0c;更新和删除为主还是以查询为主&#xff0c;如果是以插入&#xff0c;更新和删除为主的话&#xff0c;那么优化比重可以轻一点儿。 语法&#xff1a; …

Muse专业版教程:制作简谱,制作吉他谱

UP教你制作吉他谱,muse专业版吉他谱制作过程分享_哔哩哔哩_bilibili教学讲解-小宁视频制作-狂奔的琴弦软件-Muse专业版后面会分集录从零开始制作吉他谱,感兴趣的小伙伴点一波关注, 视频播放量 15457、弹幕量 1、点赞数 208、投硬币枚数 127、收藏人数 424、转发人数 59, 视频…

Day-02-02

Httpclient测试 安装HTTP Client插件 使用IDEA自带的http接口测试工具——HTTP Client Open in HTTP Client 生成测试用例 点击绿色箭头可以运行测试用例&#xff0c;控制台会输出结果。 保存和修改测试用例 在模块下新建一个api-test包用来存放测试用例&#xff0c;将生…

【测试】测试概念篇和基础篇

目 录 一.了解软件测试的基础概念1.需求2.测试用例3.BUG 二.开发模型和测试模型1.瀑布模型2.螺旋模型3.增量模型和迭代模型4.敏捷模型 三.软件测试模型V模型W模型 四.BUG篇1. 如何合理的创建 bug2. bug 级别3. bug 的生命周期4. 跟开发产生争执怎么办 一.了解软件测试的基础概念…

关于项目中websocket的socket.io客户端js库的应用

1.如何使用客户端js库? pnpm add socket.io-client2.如何建立连接&#xff1f; import io from socket.io-client // 参数1&#xff1a;不传默认是当前服务域名&#xff0c;开发中传入服务器地址 // 参数2&#xff1a;配置参数&#xff0c;根据需要再来介绍 const socket i…

Java学习第十六节之创建对象内存分析和小结类与对象

创建对象内存分析 小结类与对象 package oop;//一个项目应该只存在一个main方法 public class Application {public static void main(String[] args) {/*1.类与对象类是一个模版&#xff1a;抽象对象是一个具体的实例2.方法定义&#xff0c;调用&#xff01;3.对应的引用引用…

【vscode】在vscode中如何导入自定义包

只需要额外添加这两条语句即可&#xff1a; import os,sys sys.path.append("../..") 需要注意的是&#xff0c;ipynb 文件打开的工作目录是文件本身的路径&#xff0c;而 py 文件打开的工作路径是 vscode 打开的路径。 相比较而言 pycharm 中创建好项目之后并不…

51单片机编程应用(C语言):DS1302实时时钟

单片机计时的缺陷&#xff1a; 1.他的精度不高&#xff0c;没有时钟芯片精度高&#xff0c; 2.会占用单片机CPU的时间&#xff0c; 3.单片机的时钟无法掉电继续运行&#xff0c;&#xff08;最大的缺点&#xff09; DS1302芯片内部有备用电池&#xff0c;可以掉电继续计时…

前端JS按钮点击事件、跳出弹窗、遮罩的实战示例

前端JS 按钮事件、弹窗、遮罩实战示例 文章目录 前端JS 按钮事件、弹窗、遮罩实战示例一、开始二、功能实现三、具体代码如下1、运行结果2、具体代码如下 四、功能解析1、index.html2、button.css3、server.js 一、开始 各位未来的开发者请上座&#xff0c;闲暇的时候发现&…

蓝桥杯:C++二分算法

在基本算法中&#xff0c;二分法的应用非常广泛&#xff0c;它是一种思路简单、编程容易、效率极高的算法。蓝桥杯软件类大赛中需要应用二分法的题目很常见。 二分法有整数二分和实数二分两种应用场景 二分法的概念 二分法的概念很简单&#xff0c;每次把搜索范围缩小为上一…

普中51单片机学习(六)

点亮第一个LED LED相关知识 LED,即发光二极管&#xff0c;是一种半导体固体发光器件。工作原理为&#xff1a;LED的工作是有方向性的&#xff0c;只有当正级接到LED阳极&#xff0c;负极接到LED的阴极的时候才能工作&#xff0c;如果反接LED是不能正常工作的。其原理图如下 …

linux系统监控工具prometheus的安装以及监控mysql

prometheus 安装服务端客户端监控mysql prometheus浏览器查看 安装 https://prometheus.io/download/下载客户端和服务端以及需要监控的所有的包服务端 官网下载下载prometheustar -xf prometheus-2.47.2.linux-amd64.tar.gz -C /usr/local/ cd /usr/local/ mv prometheus-2.…

如何理解CSS的边框宽度?

CSS 边框宽度学习手记 CSS 边框宽度小概念 在CSS的世界里&#xff0c;border-width这个属性真的很实用&#xff0c;它能帮我指定HTML元素四周边框的宽度。这个宽度嘛&#xff0c;可以用像素px、点pt、厘米cm、相对单位em这些来表示&#xff0c;很方便吧&#xff01;还有呢&am…

代码随想录 Leetcode435. 无重叠区间

题目&#xff1a; 代码(首刷看解析 2024年2月17日&#xff09;&#xff1a; class Solution { private:const static bool cmp(vector<int>& a,vector<int>& b) {return a[0] < b[0];} public:int eraseOverlapIntervals(vector<vector<int>&…