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

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

1.需求背景

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

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

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

实现

1.1.具体策略活动
java 复制代码
/**
 * 具体策略活动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.所有策略的公共接口
java 复制代码
/**
 * 定义所有活动的公共接口
 */
public interface Strategy {
    void show();

}
1.3.连接策略的上下文
java 复制代码
/**
 * 定义环境角色(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.策略工厂
java 复制代码
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.测试
java 复制代码
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.抽象策略接口

java 复制代码
/**
 * 抽象策略接口
 */
public interface PromotionStrategy<R,T,U> {

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

3.2.策略具体实现类

3.2.1.春节促销策略实现类
java 复制代码
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.会员日策略实现类
java 复制代码
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.创建策略工厂

java 复制代码
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.测试

java 复制代码
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. 总结
  • 策略工厂模式: 提供了更高的灵活性和可维护性,将策略的选择和创建与客户端分离。
  • 普通策略模式: 简单直接,适用于策略较少,且客户端能够明确选择每个策略类的情况。
相关推荐
用户908324602732 天前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
willow2 天前
Axios由浅入深
设计模式·axios
用户8307196840823 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解3 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解3 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记3 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者4 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840824 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解4 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端