• 解锁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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1.2.所有策略的公共接口
    /**
     * 定义所有活动的公共接口
     */
    public interface Strategy {
        void show();
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    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();
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    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;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

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

    1. 概念
    • 策略工厂模式: 策略工厂模式是策略模式的一种变体,它将策略的选择和创建交给了工厂类来处理,客户端通过工厂获取需要的具体策略对象。
    • 普通策略模式: 普通策略模式直接由客户端选择和创建具体的策略对象,客户端需要明确知道每个策略类的存在,并负责创建相应的对象。
    2. 结构
    • 策略工厂模式: 策略工厂模式包含策略接口、具体策略类和策略工厂类。客户端通过策略工厂类获取具体的策略对象。
    • 普通策略模式: 普通策略模式包含策略接口和具体策略类。客户端直接选择和创建具体的策略对象。
    3. 使用场景
    • 策略工厂模式: 适用于需要根据条件选择不同策略,并且需要将策略选择逻辑与客户端分离的场景。适用于策略类较多,客户端不需要直接了解每个策略类的情况。
    • 普通策略模式: 适用于策略较少,客户端可以直接了解并选择每个策略类的情况。
    4. 灵活性
    • 策略工厂模式: 策略工厂模式更加灵活,可以动态地切换策略,不需要修改客户端代码。
    • 普通策略模式: 普通策略模式相对固定,客户端需要显式地选择并创建具体的策略对象。
    5. 维护性
    • 策略工厂模式: 策略工厂模式使得系统更易于扩展和维护,添加新的策略只需修改工厂类。
    • 普通策略模式: 普通策略模式需要客户端直接了解每个策略类,维护起来相对困难。
    6. 总结
    • 策略工厂模式: 提供了更高的灵活性和可维护性,将策略的选择和创建与客户端分离。
    • 普通策略模式: 简单直接,适用于策略较少,且客户端能够明确选择每个策略类的情况。
  • 相关阅读:
    像素空间文生图之Imagen原理详解
    java98-线程join使用中断进行另一个
    LeetCode(力扣)45. 跳跃游戏 IIPython
    uniApp webview 中调用底座蓝牙打印功能异常
    链上物理资产「规模化」或将推动产业协作互联网迎来爆发
    JDBC中ResultSet的使用
    【leetcode热题】比较版本号
    java中方法引用
    JavaSE 第九章 异常
    云HIS 医院综合运营管理系统源码
  • 原文地址:https://blog.csdn.net/q1372302825/article/details/136141790