Java策略模式|规则对象化
前言
- Java策略模式是经常使用的一个设计模式,其通过规则驱动,将不同的规则以及不同的实现逻辑,通过封装成类,实现消除复杂的平行规则。
- 而传统的策略模式属于类级别抽象,一个规则会抽象成类级别,策略模式虽然不复杂,但是很笨重。
- 而轻量级的策略模式,则把类级别抽象换为函数级别抽象,将相似的规则、相似的逻辑一同封装进一个函数接口中,通过Lambda表达式进行不同规则的编写。
- 这种函数级策略模式,是即时创建、即时使用、即时删除的,避免了传统策略模式导致的类爆炸,同时又沿用了策略模式的高可拓展、高可维护性。
- 函数级策略模式在特定场景下,对传统策略模式有着碾压级别的优势,但是并不意味着传统策略模式会被替代,在特定的情况下,其依旧是消除代码大规模显性规则的利器。(例如if膨胀问题)
Let's Start!
1. 传统场景模式
1. 场景描述
- 现需要一个方法,不同的手机品牌会有不同的折扣,而该折扣是分阶段实行的,给出以下几种折扣方式
品牌 | 价格区间 | 折扣率 | 附加优惠 |
小米 | ≤ 1000元 | 9.8折 | 无 |
1000元 < 价格 ≤ 2000元 | 9.5折 | 赠有线耳机 | |
价格 > 2000元 | 9.2折 | 赠小米手环 |
品牌 | 价格区间 | 折扣率 | 附加优惠 |
华为 | ≤ 1500元 | 9.5折 | 无 |
1500元 < 价格 ≤ 3000元 | 9折 | 赠FreeBuds SE | |
价格 > 3000元 | 8.5折 | 24期免息 |
品牌 | 价格区间 | 折扣率 | 附加优惠 |
iPhone | ≤ 5000元 | 无折扣 | 赠AirTag |
5000元 < 价格 ≤ 8000元 | 9.8折 | 12期免息 | |
价格 > 8000元 | 9.5折 | 24期免息 + 赠MagSafe |
2. 代码实现
- 核心方法实现
/**
* 手机服务类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Slf4j
@Service
public class MobileService {
/**
* 购买手机
*
* @param mobileBrand 手机品牌
* @param price 手机价格
* @return 购买结果
*/
public String buyMobile(String mobileBrand, Integer price) {
StringBuilder buyResult = new StringBuilder();
if (price == null) {
buyResult.append("手机价格不能为空");
return buyResult.toString();
}
if (MobileBrand.XIAOMI.getBrand().equals(mobileBrand)) {
// 小米品牌,根据价格进行购买逻辑
if (price <= 1000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D);
} else if (price <= 2000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 附赠有线耳机");
} else {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠小米手环");
}
} else if (MobileBrand.HUAWEI.getBrand().equals(mobileBrand)) {
// 华为品牌,根据价格进行购买逻辑
if (price <= 1500) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D);
} else if (price <= 3000) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠FreeBuds SE");
} else {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.85D).append(" 24期免息");
}
} else if (MobileBrand.APPLE.getBrand().equals(mobileBrand)) {
// 苹果品牌,根据价格进行购买逻辑
if (price <= 5000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(price).append(" 附赠AirTag");
} else if (price <= 8000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D).append(" 12期免息");
} else {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 24期免息 附赠MagSafe");
}
} else {
buyResult.append("手机品牌不存在");
}
return buyResult.toString();
}
}
可以看出,代码中的if有了嵌套层级,且略显臃肿,我们进行逻辑提取,进行初步优化
- 初步优化版本
/**
* 手机服务类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Slf4j
@Service
public class MobileService {
/**
* 购买手机
*
* @param mobileBrand 手机品牌
* @param price 手机价格
* @return 购买结果
*/
public String buyMobile(String mobileBrand, Integer price) {
StringBuilder buyResult = new StringBuilder();
if (price == null) {
buyResult.append("手机价格不能为空");
return buyResult.toString();
}
if (MobileBrand.XIAOMI.getBrand().equals(mobileBrand)) {
buyXiaoMi(price, buyResult);
} else if (MobileBrand.HUAWEI.getBrand().equals(mobileBrand)) {
buyHuaWei(price, buyResult);
} else if (MobileBrand.APPLE.getBrand().equals(mobileBrand)) {
buyApple(price, buyResult);
} else {
buyResult.append("手机品牌不存在");
}
return buyResult.toString();
}
/**
* 购买苹果手机
*
* @param price 价格
* @param buyResult 结果
*/
private static void buyApple(Integer price, StringBuilder buyResult) {
// 苹果品牌,根据价格进行购买逻辑
if (price <= 5000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(price).append(" 附赠AirTag");
} else if (price <= 8000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D).append(" 12期免息");
} else {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 24期免息 附赠MagSafe");
}
}
/**
* 购买华为手机
*
* @param price 手机价格
* @param buyResult 购买结果
*/
private static void buyHuaWei(Integer price, StringBuilder buyResult) {
// 华为品牌,根据价格进行购买逻辑
if (price <= 1500) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D);
} else if (price <= 3000) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠FreeBuds SE");
} else {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.85D).append(" 24期免息");
}
}
/**
* 购买小米手机
*
* @param price 手机价格
* @param buyResult 购买结果
*/
private static void buyXiaoMi(Integer price, StringBuilder buyResult) {
// 小米品牌,根据价格进行购买逻辑
if (price <= 1000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D);
} else if (price <= 2000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 附赠有线耳机");
} else {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠小米手环");
}
}
}
此时已经消除了显性的代码嵌套,但是依旧存在着复杂的if逻辑,并且也是存在着隐性if嵌套,因此我们就可以通过策略模式,来消除一部分的if逻辑,消除这种隐性的if嵌套
3. 优化思路
- 当前的逻辑流程图是这样的

可以看出,当前的流程图有着以下特点
- 在某一个节点会衍生出多种情况
- 每种情况的逻辑都是类似(判断价格区间,进行对应的购买逻辑)
- 如果是以代码翻译,就会出现多if else,在代码层面,甚至出现嵌套if,并且难以真正消除if嵌套
- 因此我们并不一定说,必须在代码层面就能看出来什么时候需要抽象,通过对流程图进行分析,或许可以更快找到优化方案
- 将不同品牌对应的价格区间判断+购买逻辑,抽象成接口:MobileBrandStrategy 然后通过定义不同的实现类:XiaoMiStrategyHuaWeiStrategy等。
- 以接口接受不同实现类,这一逻辑通过MobileFactory进行封装,随后只需要调用相关的接口方法,即可获取到对应的购买结果
- 通过这样的优化,我们可以将第一层选择:根据不同的品牌选择不同的策略方法,抽象成为类级别,消除了第二层的if。后续再添加其他的业务逻辑(例如添加红米手机的折扣策略),只需要添加类方法,而不需要在原方法内部进行if分支的增加。提高了可拓展性
- 因此,策略模式的优势就是可以消除最外层的if条件选择,并且符合开闭原则,也就是说,无需在方法层面上进行代码的更改(即使是添加代码,不删改,也是不符合开闭原则的),只需要添加新的类,修改工厂类中的创建对象逻辑,即可完成业务逻辑的拓展。
- 当然,这里依旧有些缺陷,即需要在工厂方法中进行对应的修改,并且这里也涉及到了if逻辑,这种优化是不彻底的,本质上是将最外层的分支选择,抽象成了针对策略类的选择。因此后续我们也会继续讲解针对这里的优化逻辑
4. 进一步改造
- 策略接口提供
/**
* 手机品牌策略类,专注于针对不同的手机品牌进行不同的策略方式选择
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
public interface MobileBrandStrategy {
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 手机购买结果
* @return 手机购买结果
*/
StringBuilder buyMobile(Integer price, StringBuilder buyResult);
}
- 策略类提供
/**
* 基于小米品牌实现的策略类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Component
public class XiaoMiStrategy implements MobileBrandStrategy {
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 购买结果
* @return 手机购买结果
*/
@Override
public StringBuilder buyMobile(Integer price, StringBuilder buyResult) {
// 小米品牌,根据价格进行购买逻辑
if (price <= 1000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D);
} else if (price <= 2000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 附赠有线耳机");
} else {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠小米手环");
}
return buyResult;
}
}
/**
* 基于华为品牌实现的策略类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Component
public class HuaWeiStrategy implements MobileBrandStrategy {
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 手机购买结果
* @return 手机购买结果
*/
@Override
public StringBuilder buyMobile(Integer price, StringBuilder buyResult) {
// 华为品牌,根据价格进行购买逻辑
if (price <= 1500) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D);
} else if (price <= 3000) {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠FreeBuds SE");
} else {
buyResult.append("购买华为手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.85D).append(" 24期免息");
}
return buyResult;
}
}
/**
* 基于苹果品牌的策略方式
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Component
public class AppleStrategy implements MobileBrandStrategy {
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 手机购买结果
* @return 手机购买结果
*/
@Override
public StringBuilder buyMobile(Integer price, StringBuilder buyResult) {
// 苹果品牌,根据价格进行购买逻辑
if (price <= 5000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(price).append(" 附赠AirTag");
} else if (price <= 8000) {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D).append(" 12期免息");
} else {
buyResult.append("购买苹果手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 24期免息 附赠MagSafe");
}
return buyResult;
}
}
- 工厂类提供
/**
* 手机品牌策略工厂类
* <p>
* 该工厂类根据传入的手机品牌名称,返回对应的手机品牌策略实现类。
* 支持的品牌包括:小米、华为、苹果。
* </p>
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
public class MobileStrategyFactory {
/**
* 私有构造函数,防止外部实例化
*/
private MobileStrategyFactory() {}
/**
* 根据手机品牌名称获取对应的策略实现类
* <p>
* 通过比较传入的品牌名称与枚举类中定义的品牌名称,返回相应的策略对象。
* 如果没有匹配的品牌,则返回 null。
* </p>
*
* @param mobileBrand 手机品牌名称
* @return 对应的手机品牌策略实现类,如果未找到则返回 null
*/
public static MobileBrandStrategy getMobileStrategy(String mobileBrand) {
if (MobileBrand.XIAOMI.getBrand().equals(mobileBrand)) {
return new XiaoMiStrategy();
} else if (MobileBrand.HUAWEI.getBrand().equals(mobileBrand)) {
return new HuaWeiStrategy();
} else if (MobileBrand.APPLE.getBrand().equals(mobileBrand)) {
return new AppleStrategy();
} else {
return null;
}
}
}
- 核心类提供
/**
* 手机服务类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Slf4j
@Service
public class MobileService {
/**
* 购买手机
*
* @param mobileBrand 手机品牌
* @param price 手机价格
* @return 购买结果
*/
public String buyMobile(String mobileBrand, Integer price) {
StringBuilder buyResult = new StringBuilder();
if (price == null) {
buyResult.append("手机价格不能为空");
return buyResult.toString();
}
MobileBrandStrategy mobileStrategy = MobileStrategyFactory.getMobileStrategy(mobileBrand);
buyResult = Objects.isNull(mobileStrategy) ? buyResult.append("手机品牌不能为空") : mobileStrategy.buyMobile(price, buyResult);
return buyResult.toString();
}
}
5. 改造后总结
- 改造后的流程图如下

- 优化后,品牌选择逻辑封装到工厂中
- 所有策略实现统一接口
- 价格区间判断从主流程下沉到各策略内部
- 新增品牌只需在策略实现子图中添加节点
6. 最终优化方案
优化思路
- 上一次我们提到了工厂中依旧存在着根据if进行不同条件的返回。
- 也就是说,当我们新增一个策略类,仍旧需要进行if-else的分支增加。
- 换句话说,我们依旧是需要使用if-else的普通映射模式,但是我们可以更换一种方式,让工厂类实现自动注入映射关系。
- 也就是说,通过反射+Map,实现一对一映射,从而彻底消除if-else,实现最终优化。
- 而SpringBoot可以直接注入一个接口下的所有实现类,只要实现类进行了Bean注册,即可通过构造方法进行自动化注入。
最终优化代码
- 对应的接口改动
/**
* 手机品牌策略类,专注于针对不同的手机品牌进行不同的策略方式选择
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
public interface MobileBrandStrategy {
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 手机购买结果
* @return 手机购买结果
*/
StringBuilder buyMobile(Integer price, StringBuilder buyResult);
/**
* 获取手机品牌
*
* @return 手机品牌
*/
String getBrand();
}
- 其中一个类的改动(其他的类遵循类似改动即可)
/**
* 基于小米品牌实现的策略类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Data
@Component
public class XiaoMiStrategy implements MobileBrandStrategy {
private String brand = MobileBrand.XIAOMI.getBrand();
/**
* 手机购买策略
*
* @param price 手机价格
* @param buyResult 购买结果
* @return 手机购买结果
*/
@Override
public StringBuilder buyMobile(Integer price, StringBuilder buyResult) {
// 小米品牌,根据价格进行购买逻辑
if (price <= 1000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.98D);
} else if (price <= 2000) {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.95D).append(" 附赠有线耳机");
} else {
buyResult.append("购买小米手机成功,").append("手机价格: ").append(Double.valueOf(price) * 0.9D).append(" 附赠小米手环");
}
return buyResult;
}
}
- 工厂改动(通过Spring以来机制进行注入)
/**
* 手机品牌策略工厂类
* <p>
* 该工厂类根据传入的手机品牌名称,返回对应的手机品牌策略实现类。
* 支持的品牌包括:小米、华为、苹果。
* </p>
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Component
public class MobileStrategyFactory {
/**
* 存储手机品牌与对应策略实现类映射关系的静态Map
* key: 手机品牌名称
* value: 对应的手机品牌策略实现类
*/
private static final Map<String, MobileBrandStrategy> MOBILE_BRAND_STRATEGY_MAP;
// 静态初始化块,初始化策略映射Map
static {
MOBILE_BRAND_STRATEGY_MAP = new HashMap<>();
}
/**
* 构造函数,通过Spring注入的所有手机品牌策略实现类列表来初始化策略映射Map
*
* @param mobileBrandStrategies Spring容器中所有实现了MobileBrandStrategy接口的Bean列表
*/
public MobileStrategyFactory(List<MobileBrandStrategy> mobileBrandStrategies) {
// 遍历所有手机品牌策略实现类
for (MobileBrandStrategy mobileBrandStrategy : mobileBrandStrategies) {
// 获取策略实现类对应的手机品牌
String brand = mobileBrandStrategy.getBrand();
// 将手机品牌与对应的策略实现类存入映射Map中
MOBILE_BRAND_STRATEGY_MAP.put(brand, mobileBrandStrategy);
}
}
/**
* 根据手机品牌名称获取对应的策略实现类
* <p>
* 通过比较传入的品牌名称与已注册的品牌名称,返回相应的策略对象。
* 如果没有匹配的品牌,则返回 null。
* </p>
*
* @param mobileBrand 手机品牌名称
* @return 对应的手机品牌策略实现类,如果未找到则返回 null
*/
public MobileBrandStrategy getMobileStrategy(String mobileBrand) {
// 从映射Map中获取对应品牌的策略实现类
return MOBILE_BRAND_STRATEGY_MAP.get(mobileBrand);
}
}
- 核心类实现
/**
* 手机服务类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MobileService {
private final MobileStrategyFactory mobileStrategyFactory;
/**
* 购买手机
*
* @param mobileBrand 手机品牌
* @param price 手机价格
* @return 购买结果
*/
public String buyMobile(String mobileBrand, Integer price) {
StringBuilder buyResult = new StringBuilder();
if (price == null) {
buyResult.append("手机价格不能为空");
return buyResult.toString();
}
MobileBrandStrategy mobileStrategy = mobileStrategyFactory.getMobileStrategy(mobileBrand);
buyResult = Objects.isNull(mobileStrategy) ? buyResult.append("手机品牌不能为空") : mobileStrategy.buyMobile(price, buyResult);
return buyResult.toString();
}
}
对应流程图

7. 总结
- 策略模式,是通过将流程图中的多个可选择,且逻辑类似的平行节点抽象成类,不同的策略类负责对应的单一职责,从而消除了显性的内部逻辑。
- 又通过工厂模式,封装不同策略类选择的过程,将最外层的节点选择流程进行隐藏,从而实现了基础版本的策略模式。
- 而通过Spring的依赖注入机制,将对应的策略接口下的所有策略实现类进行依赖注入,在初始化构造函数中实现好对Map的存储。从而实现对工厂的映射模式的底层改进(if/switch分支选择 ——> Hash映射)
- 通过这种修改,实现了将复杂的分支选择,转化为Hash映射,以及对应的分支的逻辑,下沉到对应的实现类中,如果要拓展新的分支方法,只需要添加新方法,符合开闭原则,大大提高了代码的可维护性以及可拓展性。
2. 函数级策略模式
1. 场景描述
同1.1一样,我们依旧是通过这种形式来进行讲述,并进行函数级别优化,最后我们进行对比,说明这两种策略模式都适合在什么时候进行使用。
2. 优化思路
- 当前传统的策略模式,其主要思路就是通过将具有相似逻辑的平行节点,抽象成一个接口,再对应每个节点进行对应实现。并通过工厂模式,使用Map形式进行O(1)级别的映射。
- 但是有一个问题就是,当流程几乎固定,且使用次数不超过三次,以及对应的平行节点不断增加,那么对应的策略类也会发生膨胀,而其内部的逻辑几乎是固定的,这也就导致加载过程中,要进行大量的类级别加载,而内部逻辑流程固定,会产生资源浪费。
- 针对这一种情况,我们可以进一步进行抽象,将类级别的抽象,换成函数级别的抽象,当流程几乎固定不变,且不会很复杂的时候,函数级策略模式就起到了更佳的优势。
- 针对我们当前的场景:每个品牌的折扣方式几乎固定(都是分阶段进行不同折扣,以及不同的附加优惠)。那么我们可以在传统策略实现的基础上,进一步进行抽象。
- 三个手机品牌会对价格进行区间定位,根据不同的区间定位,实现不同的折扣策略。也就是说,我们可以抽象出来一个函数接口列表,返回值为Boolean,根据其满足的区间,进行不同级别的折扣策略。
- 新版流程图如下

3. 对应代码实现
- 规则类实现(相似流程,函数级别抽象,更轻量)
/**
* 手机品牌折扣规则类,通过函数式接口实现策略模式的轻量级替换
* 该类封装了不同品牌的折扣规则,支持动态匹配和计算折扣价格
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Data
@AllArgsConstructor
public class MobileBrandAccountRule {
/**
* 折扣条件判断函数列表,每个函数接收价格参数,返回是否满足折扣条件
*/
private List<Function<Integer, Boolean>> isAccounts;
/**
* 折扣率列表,与isAccounts一一对应,表示满足条件时的价格折扣倍数
*/
private List<Double> accounts;
/**
* 优惠结果描述列表,与isAccounts一一对应,表示满足条件时的优惠说明
*/
private List<String> accountResults;
/**
* 手机品牌名称
*/
private String mobileBrand;
/**
* 根据手机价格和折扣规则计算购买结果
* 遍历所有折扣规则,找到第一个满足条件的规则并应用折扣
*
* @param price 手机原价
* @param sb 结果构建器,用于拼接购买结果信息
* @return 包含购买结果信息的StringBuilder对象
*/
public StringBuilder buyMobile(Integer price, StringBuilder sb) {
int size = isAccounts.size();
for (int i = 0; i < size; i++) {
Function<Integer, Boolean> isAccount = isAccounts.get(i);
Boolean inAccount = isAccount.apply(price);
if (inAccount) {
sb.append("手机购买成功, ")
.append("手机价格: ")
.append(Double.valueOf(price) * accounts.get(i))
.append(",")
.append("优惠附加: ")
.append(accountResults.get(i));
return sb;
}
}
return sb.append("手机购买失败");
}
}
- 工厂封装(需要添加实现类,只需要在工厂中添加对应的实现方法)
/**
* 手机品牌折扣规则工厂类
* 该工厂负责创建和管理不同品牌的折扣规则,通过策略模式实现规则的灵活扩展
* 如果要添加新品牌实现,只需要在工厂中添加对应的实现方法
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
public class MobileBrandAccountRuleFactory {
/**
* 手机品牌折扣规则缓存
*/
private MobileBrandAccountRuleFactory() {}
/**
* 存储各品牌折扣规则的映射表
* key: 品牌名称
* value: 对应的折扣规则对象
*/
private static final Map<String, MobileBrandAccountRule> MOBILE_BRAND_ACCOUNT_RULE_MAP;
static {
// 初始化各品牌折扣规则映射表
MOBILE_BRAND_ACCOUNT_RULE_MAP = Map.of(
// 小米品牌折扣规则配置
MobileBrand.XIAOMI.getBrand(),
new MobileBrandAccountRule(
// 价格区间判断条件列表
List.of(
// 价格小于等于1000元
price -> price <= 1000,
// 价格小于等于2000元
price -> price <= 2000,
// 价格大于2000元
price -> price > 2000
),
// 对应的折扣率列表
List.of(
// 98折
0.98D,
// 95折
0.95D,
// 92折
0.92D
),
// 对应的优惠赠品列表
List.of(
// 无赠品
"",
// 赠送有线耳机
"赠有线耳机",
// 赠送小米手环
"赠小米手环"
),
MobileBrand.XIAOMI.getBrand()
),
// 华为品牌折扣规则配置
MobileBrand.HUAWEI.getBrand(),
new MobileBrandAccountRule(
// 价格区间判断条件列表
List.of(
// 价格小于等于1500元
price -> price <= 1500,
// 价格小于等于3000元
price -> price <= 3000,
// 价格大于3000元
price -> price > 3000
),
// 对应的折扣率列表
List.of(
// 98折
0.98D,
// 95折
0.95D,
// 92折
0.92D
),
// 对应的优惠赠品列表
List.of(
// 无赠品
"",
// 赠送FreeBuds SE耳机
"赠FreeBuds SE",
// 提供24期免息分期
"24期免息"
),
MobileBrand.HUAWEI.getBrand()
),
// 苹果品牌折扣规则配置
MobileBrand.APPLE.getBrand(),
new MobileBrandAccountRule(
// 价格区间判断条件列表
List.of(
// 价格小于等于5000元
price -> price <= 5000,
// 价格小于等于8000元
price -> price <= 8000,
// 价格大于8000元
price -> price > 8000
),
// 对应的折扣率列表
List.of(
// 无折扣
1.00D,
// 98折
0.98D,
// 95折
0.95D
),
// 对应的优惠赠品列表
List.of(
// 赠送AirTag
"赠AirTag",
// 提供12期免息分期
"12期免息",
// 提供24期免息分期并赠送MagSafe
"24期免息 + 赠MagSafe"
),
MobileBrand.APPLE.getBrand()
)
);
}
/**
* 根据品牌名称获取对应的折扣规则
*
* @param brand 品牌名称
* @return 对应的折扣规则对象,如果找不到则返回null
*/
public static MobileBrandAccountRule getMobileBrandAccountRule(String brand) {
return MOBILE_BRAND_ACCOUNT_RULE_MAP.get(brand);
}
}
- 核心类使用(几乎无改动)
/**
* 手机服务类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MobileService {
/**
* 购买手机
*
* @param mobileBrand 手机品牌
* @param price 手机价格
* @return 购买结果
*/
public String buyMobile(String mobileBrand, Integer price) {
StringBuilder buyResult = new StringBuilder();
if (price == null) {
buyResult.append("手机价格不能为空");
return buyResult.toString();
}
MobileBrandAccountRule mobileBrandAccountRule = MobileBrandAccountRuleFactory.getMobileBrandAccountRule(mobileBrand);
buyResult = Objects.isNull(mobileBrandAccountRule) ? buyResult.append("手机品牌不能为空") : mobileBrandAccountRule.buyMobile(price, buyResult);
return buyResult.toString();
}
}
4. 当前示例与传统策略模式对比
场景 | 函数级策略模式 | 传统策略模式 | 补充 |
实现复杂度 | 高 | 低 | 该示例中,函数级策略模式需要抽象设计编排流程,而传统策略模式只需要按照不同的策略方式直接写代码即可 |
加载消耗 | 低 | 高 | 函数级策略模式不需要进行策略类加载,而传统策略模式如果类膨胀,性能消耗明显 |
可拓展性 | 高 | 高 | 函数级策略模式中,如果需要添加新的策略,只需要在工厂的初始化中直接添加即可 传统策略模式中,如果需要添加新的策略,只需要再创建新的实现类即可 |
简洁性 | 高 | 中 | 函数级策略模式只需要针对函数级别进行不同的输入即可 而传统策略模式则需要构建完整的类,代码两略多 |
总结来看,当前场景下,二者其实相差无几,是因为当前的场景的特点如下:
- 流程略复杂但是固定
- 流程可以抽象(区间定位)
- 后续的处理较为简单(进行打折,输出结果)
这些情况,正好处于二者适用范围的交界处,因此二者会展现出来不相上下的情况。
但是当情况略有改动,二者的区分就很明显了。
5. 换一个例子
前言介绍
场景描述:通过Redis的info memory命令,获取到对应的内存信息。实现性能监控。针对于某些重要的属性,进行阈值判断,如果超过阈值,就加入到字符串中作为警告。
简单分析:当前场景,如果要实现,会有多个if,但是并不会出现上述的if-else,这是否也可以通过策略类实现?是可以的,其有十分明显的一个特点:多个if的执行顺序可以替换,也就是说,其节点是平行的,而且是允许多个节点同时进行。
换句话说,传统策略模式,是多个节点中选择单个节点执行。而当前场景,则是多个节点中可以选择任意多个节点执行。不过其不再适合通过工厂模式返回对应的单个策略类,而是可以通过策略类内部的条件判断,进行过滤、执行。
也就是说:策略模式并不是死板的,而是灵活多变,判断时机,就是多节点平行,再通过单节点执行还是任意多节点执行,进行进一步选择。而具体到选择函数级策略还是传统的,就需要再次细分。
原始代码
/**
* 监控告警
* 返回Redis内存监控指标的告警信息
*
* @return 告警信息
*/
@Override
public String monitorAlerts() {
StringBuilder sb = new StringBuilder();
int memoryPercent = queryUsedMemoryPercent();
double memFragmentationRatio = calculateFragmentationRatio();
long evictedKeys = getEvictedKeysCount();
double fragmentationRisk = calculateFragmentationRiskLevel();
double evictionRisk = calculateEvictionRiskIndicator();
if (memoryPercent > MEMORY_PERCENT_THRESHOLD) {
sb.append(sb.isEmpty() ? "" : ", ").append("内存使用率过高: ").append(memoryPercent);
}
if (memFragmentationRatio > MEM_FRAGMENTATION_RATIO_THRESHOLD) {
sb.append(sb.isEmpty() ? "" : ", ").append("内存碎片率过高: ").append(memFragmentationRatio);
}
if (evictedKeys > 0) {
sb.append(sb.isEmpty() ? "" : ", ").append("键驱逐数量: ").append(evictedKeys);
}
if (fragmentationRisk > FRAGMENTATION_RISK_THRESHOLD) {
sb.append(sb.isEmpty() ? "" : ", ").append("内存碎片风险等级: ").append(fragmentationRisk);
}
if (evictionRisk > EVICTION_RISK_THRESHOLD) {
sb.append(sb.isEmpty() ? "" : ", ").append("内存驱逐风险等级: ").append(evictionRisk);
}
return sb.toString();
}
流程图

6. 针对新例子的优化
我们可以看出,这个方法中的多if,是可以调换,在流程图中展现的形式,就是平行,可以根据条件,选择任意多节点同时执行。但是这里会显得臃肿,因为if很多,规则类似,导致复用性差。
因此我们依旧可以使用策略模式进行优化,而这里我们使用函数级进行策略模式进行优化,方法级别和函数级别转换其实也十分容易。根据创建的函数,与对应的方法进行转换。这里就需要考验开发者的代码能力,如何将二者转换。这里上述有对应示例(类级—>方法级),可以参考一下。
- 核心类实现
/**
* 简单规则+message的整合类
*
* @author 王玉涛
* @version 1.0
* @since 2025/8/12
*/
@Data
@AllArgsConstructor
public class AlertRule {
/**
* 告警判断条件,通过Supplier函数式接口实现动态判断
* 返回true表示触发告警,false表示不触发告警
*/
private Supplier<Boolean> isAlert;
/**
* 告警信息内容,当告警条件满足时需要输出的信息
*/
private String message;
/**
* 检查并展示告警信息
* 当告警条件满足时,将告警信息追加到StringBuilder中
* 如果StringBuilder中已有内容,则先添加逗号分隔符
*
* @param sb 用于收集告警信息的StringBuilder对象,不可为null
*/
public void showAlert(StringBuilder sb) {
if (Boolean.TRUE.equals(isAlert.get())) {
sb.append(sb.isEmpty() ? "" : ",");
sb.append(message);
}
}
}
- 对应方法优化
/**
* 监控告警
* 返回Redis内存监控指标的告警信息
*
* @return 告警信息
*/
@Override
public String monitorAlerts() {
// 创建StringBuilder用于收集告警信息
StringBuilder sb = new StringBuilder();
// 获取各项监控指标值
int memoryPercent = queryUsedMemoryPercent();
double fragmentationRisk = calculateFragmentationRiskLevel();
double evictionRisk = calculateEvictionRiskIndicator();
// 定义告警规则列表,每个规则包含判断条件和告警信息
List<AlertRule> alertRules = Arrays.asList(
// 内存使用率过高告警:当内存使用百分比超过阈值时触发
new AlertRule(() -> memoryPercent > MEMORY_PERCENT_THRESHOLD, "内存使用率过高: " + memoryPercent),
// 内存碎片风险告警:当内存碎片比率超过阈值时触发
new AlertRule(() -> memFragmentationRatio > MEM_FRAGMENTATION_RATIO_THRESHOLD, "内存碎片风险: " + memFragmentationRatio),
// 内存驱逐告警:当存在因内存不足被驱逐的键时触发
new AlertRule(() -> evictedKeys > 0, "存在因内存不足被驱逐的键数量: " + evictedKeys),
// 内存碎片风险等级过高告警:当碎片风险等级超过阈值时触发
new AlertRule(() -> fragmentationRisk > FRAGMENTATION_RISK_THRESHOLD, "内存碎片风险等级过高: " + fragmentationRisk),
// 内存驱逐风险等级过高告警:当驱逐风险等级超过阈值时触发
new AlertRule(() -> evictionRisk > EVICTION_RISK_THRESHOLD, "内存驱逐风险等级过高: " + evictionRisk)
);
// 遍历告警规则,筛选出满足条件的规则并执行告警信息收集
alertRules.stream()
// 筛选触发告警的规则
.filter(alertRule -> alertRule.getIsAlert().get())
// 执行告警信息收集
.forEach(alertRule -> alertRule.showAlert(sb));
// 返回最终的告警信息字符串
return sb.isEmpty() ? "监控指标正常!" : sb.toString();
}
通过以上优化,我们成功将多个平行if消除,并且提升了拓展性,针对这种复用性不高、逻辑类似、规则简单的情况下,对比传统的策略模式有着碾压级别优势。因为其更加轻量,更加适合当前业务场景。
3. 总结
策略模式是消除if的利器,但是我们要明白一个原则,不要为了炫技而胡乱使用策略模式,因为设计模式通常都是重量级的,并且也会在一定程度上,降低可读性,需要一定的技术门槛。如果使用不当,就会徒增复杂度。因此使用策略模式之前,我们需要权衡一下:使用策略模式消除if后,其带来的收益(可维护性,可拓展性)是否超过了引入设计模式而带来的复杂度提升。
- 简单来说,策略模式的引用时机,就是当一个流程中,出现了平行的多节点,且多节点的逻辑相似。我们可以考虑引入设计模式。
- 当我们考虑引入设计模式后,需要观察该平行节点,是可以多节点并行,还是单节点串行(是if-else-if,还是纯if),通过这一点,我们考虑是通过工厂模式+Map一对一映射来完成,还是通过stream流+filter进行多节点同时进行
- 进一步,我们也需要判断当前的节点规模,当明确会<3个,且不会增长时候,不推荐使用策略模式。而当节点不少于3个,且逻辑可以抽象时候,推荐使用策略模式。
- 然后再根据节点内部的复杂度,考虑是用传统策略模式,还是使用函数级策略模式。当流程相对固定,可以进行抽象提取时候,推荐使用函数级别策略模式,其虽然会考验开发者的抽象能力+代码设计能力。但是在这种轻量级别场景对传统方式有着碾压级别优势。而当内部逻辑复杂,几乎无法提取共性时候,就推荐传统策略模式。
- 这两种策略模式并不是互斥,而是在轻量级和重量级场景中形成互补。通过这一套方法论,即使是开发初期,开发者也可以迅速判断当前情景是否适合采用策略模式。适合什么级别的设计模式。从而直接提高项目的可维护性。
- 决策流程图如下


