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

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. 总结
  • 策略工厂模式: 提供了更高的灵活性和可维护性,将策略的选择和创建与客户端分离。
  • 普通策略模式: 简单直接,适用于策略较少,且客户端能够明确选择每个策略类的情况。

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

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

相关文章

Docker数据卷容器(容器继承)

Docker数据卷容器&#xff08;容器继承&#xff09; 创建DockerFile构建镜像启动容器修改数据卷创建子容器验证 命名的容器挂载数据卷。其他容器通过挂载这个容器实现数据共享&#xff0c;挂载数据的容器 -> 称之为数据卷容器 创建DockerFile FROM centosVOLUME ["dat…

哪个牌子的洗地机好用?性能超好的洗地机推荐

洗地机已经是家庭中不可或缺的清洁家电之一了&#xff0c;它的出现极大地方便了我们的生活&#xff0c;并为我们解决了一大难题&#xff1a;洗地板的繁琐。无论是家庭主妇还是上班族&#xff0c;对于洗地机的需求都是无可替代的。随着科技的不断进步&#xff0c;洗地机的功能也…

上门回收小程序开发,互联网下发展机遇

在当下生活水平大幅度上升发展下&#xff0c;回收成为了人们日常生活中的一部分。 如今&#xff0c;随着互联网的快速发展&#xff0c;回收行业也进行了升级换代&#xff0c;由传统的线下回收门店到回收箱在到当下的线上互联网回收模式&#xff0c;迈向了“互联网废品回收”的…

安装ts-node有感

起因&#xff1a;想要在vsCode上运行ts脚本 解决方案&#xff1a; 1.安装vsCode插件 code runner 2.全局安装ts-node 这一步遇到三个问题&#xff1a; ①.node版本问题&#xff1a;需安装版本18以上node&#xff0c;可使用nvm去控制不同的node版本 ②.certificate has exp…

【以解决】Pyinstaller打包报错IndexError: tuple index out of range

问题 这个问题主要是在Python3.7以上的版本中遇到&#xff0c;用pyinstaller打包的时候发现报错 (pyinstallerEnv) D:\virtualEnv\pyinstallerEnv\Scripts>auto-py-to-exe pygame 2.5.2 (SDL 2.28.3, Python 3.10.0) Hello from the pygame community. https://www.pygame…

PCIe TX端电容

一、问题&#xff1a;PCIe为什么要加电容 PCIe为什么要加电容&#xff1f;具体的作用是什么&#xff1f; 答&#xff1a;因为PCIe Host和Receiver两端的直流偏置会不一样&#xff0c;所以需要在PCIe的传输路径上加电容&#xff0c;这样传输路径上只有AC信号&#xff0c;不存在…

const--类的常量成员函数

在C中,为了禁止成员函数修改数据成员的值,可以将它设置为常量成员函数。设置常量成员函数的方法是在函数原型的后面加上const,形式如下: class x { …………… T f(t1,t2) const{} ………… }; 常量成员函数的作用&#xff1a; 将成员函数设置为const&#xff0c;表明该成员函…

MySQL跨服务器关联查询

1. 首先确认服务器的Federated引擎是否开启 show engines;修改数据库的配制文件my.ini,(我的my.ini的路径为&#xff1a;D:\ProgramData\MySQL\MySQL Server 5.7/my.ini),将federated添加到my.ini文件中 到MySQL的my.cnf配置文件中修改 在 [mysqld] 下方加入 federated 然后重…

CentOS 7.9如何禁止内核自动更新升级

要在 CentOS 7.9 系统中禁止内核自动更新&#xff0c;你可以通过配置 YUM&#xff08;Yellowdog Updater, Modified&#xff09;来实现。这里有几种方法可以阻止内核自动更新&#xff1a; 方法 1: 使用 exclude 选项在 YUM 配置中 编辑 YUM 的配置文件 /etc/yum.conf&#xff…

[SWPUCTF 2021 新生赛]crypto8

第一眼看见是乱码不确定是什么的编码 看了下感觉是UUencode编码 UUencode编码是一种古老的编码方式&#xff0c;通常用于将二进制数据转换成可打印字符的形式。UUencode编码采用一种基于64个字符的编码表&#xff0c;将每3个字节的数据编码为4个可打印字符&#xff0c;以实现…

C++类和对象——继承详解

目录 1.基本语法 2.继承方式 3.继承中的对象模型 4.构造和构析顺序 5.同名成员处理 6.同名静态成员处理 7.多继承语法 8.菱形继承 图片示例&#xff1a; 虚继承 代码示例&#xff1a; 1.基本语法 #include<bits/stdc.h> using namespace std;//公共页面类 …

心法利器[107] onnx和tensorRT的bert加速方案记录

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2023年新一版的文章合集已经发布&#xff0c;获取方式看这里&#xff1a;又添十万字-CS的陋室2…

深度学习基础之《TensorFlow框架(4)—Operation》

一、常见的OP 1、举例 类型实例标量运算add&#xff0c;sub&#xff0c;mul&#xff0c;div&#xff0c;exp&#xff0c;log&#xff0c;greater&#xff0c;less&#xff0c;equal向量运算concat&#xff0c;slice&#xff0c;splot&#xff0c;canstant&#xff0c;rank&am…

Sora OpenAI 101教程(一):从文本生成令人兴奋的视频的 AI 模型

想象一下&#xff0c;您可以通过简单的文本提示创建令人惊叹的视频&#xff0c;例如“一个人带着狗在月球上行走”。听起来不可能&#xff0c;对吧&#xff1f;好吧&#xff0c;现在不再这样了&#xff0c;感谢 OpenAI 的最新人工智能模型 Sora&#xff0c;它可以从文本生成令人…

基于QEMU的vexpress-a9的初始化代码运行(二)

这个part是想详细走读一下用qemu运行kernel的最初始代码&#xff0c;也就是使用qemu运行kernel代码的详细逻辑&#xff0c;从qemu加载根目录下vmlinux镜像的逻辑&#xff0c;也就是运行arch/arm/kernel/head.S的整个过程&#xff0c;直到跳转到start_kernel&#xff0c;使用的k…

GraphPad Prism 10.2.0 for Mac 强大实用的医学绘图分析工具

GraphPad Prism GraphPad Prism是一款非常实用的统计软件&#xff0c;其功能非常强大&#xff0c;能够帮助用户进行各类科研数据的处理和分析&#xff0c;快速绘制出各种专业的图像和数据报告。 GraphPad Prism软件的用户界面非常友好&#xff0c;易于学习和操作&#xff0c;…

通过前序和中序遍历结果构造二叉树

题目 105. 从前序与中序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 思路 首先思考&#xff0c;根节点应该做什么。 肯定要想办法确定根节点的值&#xff0c;把根节点做出来&#xff0c;然后递归构造左右子树即可。 我们先来回顾一下&#xff0c;前序遍历和…

使用MinIO S3存储桶备份Weaviate

Weaviate 是一个开创性的开源向量数据库&#xff0c;旨在通过利用机器学习模型来增强语义搜索。与依赖关键字匹配的传统搜索引擎不同&#xff0c;Weaviate 采用语义相似性原则。这种创新方法将各种形式的数据&#xff08;文本、图像等&#xff09;转换为矢量表示形式&#xff0…

分享一下,程序员为什么不喜欢关电脑?(个人观点仅供娱乐哈哈哈)

你是否曾经疑惑&#xff0c;为何身边的程序员朋友总是让电脑保持开机状态&#xff0c;仿佛与它们有着不解之缘&#xff1f;别急着给他们贴上“电脑迷”的标签&#xff0c;背后其实隐藏着许多合理的原因。今天&#xff0c;就让我们一同走进程序员的世界&#xff0c;探究他们为何…

My desktop didn‘t come with the Bluetooth.

You didnt turn on the Bluetooth on PC and phone.Turn on it to control your phone. My desktop didnt come with the Bluetooth. 电脑控制手机的时候&#xff0c;电脑蓝牙没打开 电脑蓝牙打开步骤 电脑蓝牙的小图标打开了 手机上可以看到计算机了【Thinkpad-T440p-zwf】 无…