策略模式(长文讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
- 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在软件开发中,我们常常需要根据不同的条件或场景执行不同的逻辑。例如,电商平台的优惠券规则、游戏中的角色攻击方式、甚至是交通信号灯的控制逻辑,这些场景都可能因为条件变化而产生多种行为分支。如何将这些复杂的条件判断逻辑清晰地组织起来,同时保持代码的可维护性和扩展性?这正是 策略模式 的核心价值所在。
策略模式是一种行为型设计模式,它通过封装算法或行为的不同变体,并让这些变体能够在运行时动态切换。本文将从基础概念、实现原理、实际案例及代码示例等角度,逐步解析策略模式的设计思想,帮助开发者掌握这一模式在实际项目中的应用。
策略模式的定义与核心思想
定义
策略模式(Strategy Pattern)允许一个类的行为在运行时根据需要动态选择不同的算法或策略。其核心是将算法的实现与使用算法的客户端解耦,使得算法的变化不会影响客户端代码。
核心思想:“算法家族”与“委托决策”
- 算法家族:将所有可能的行为变体抽象为一个共同的接口,每个具体实现类代表一种策略。
- 委托决策:客户端不再直接处理复杂的条件判断,而是将决策权交给策略对象,通过组合或依赖注入的方式调用策略。
形象比喻:交通灯的控制逻辑
想象一个十字路口的交通灯控制系统:
- 红灯:所有车辆停止。
- 绿灯:直行车辆通行。
- 左转箭头绿灯:左转车辆通行。
这些不同的规则可以看作是不同的“策略”,而交通灯控制器(客户端)只需要根据当前时间或传感器数据选择对应的策略即可,无需在代码中硬编码复杂的条件分支。
策略模式的实现步骤
以下是实现策略模式的典型步骤:
1. 定义策略接口
首先,定义一个公共接口或抽象类,声明所有策略必须实现的方法。例如:
public interface TrafficLightStrategy {
void execute();
}
2. 创建具体策略类
为每种行为变体创建具体的实现类:
public class RedLightStrategy implements TrafficLightStrategy {
@Override
public void execute() {
System.out.println("红灯亮起,所有车辆停止!");
}
}
public class GreenLightStrategy implements TrafficLightStrategy {
@Override
public void execute() {
System.out.println("绿灯亮起,直行车辆通行!");
}
}
3. 创建上下文(Context)类
上下文类持有策略接口的引用,并提供方法供客户端切换策略:
public class TrafficLightContext {
private TrafficLightStrategy strategy;
public void setStrategy(TreatmentLightStrategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
if (strategy != null) {
strategy.execute();
}
}
}
4. 客户端使用策略
在客户端代码中,根据条件动态选择策略:
public class Client {
public static void main(String[] args) {
TrafficLightContext context = new TrafficLightContext();
// 根据时间选择策略
int currentTime = 15; // 15:00
if (currentTime < 18) {
context.setStrategy(new GreenLightStrategy());
} else {
context.setStrategy(new RedLightStrategy());
}
context.executeStrategy();
}
}
策略模式的优势与适用场景
优势分析
- 消除条件判断嵌套:将条件分支逻辑集中到策略类中,避免客户端代码中出现大量的
if-else
或switch
语句。 - 提高扩展性:新增策略只需实现接口,无需修改现有代码,符合开闭原则(对扩展开放,对修改关闭)。
- 提升代码复用性:相同策略可以在不同上下文中复用。
- 清晰的职责分离:策略接口与具体实现分离,降低模块间的耦合度。
适用场景
- 多条件分支场景:当业务逻辑因不同条件产生多种行为时(如支付方式、排序算法、权限校验等)。
- 算法需要频繁切换:例如游戏中的角色攻击模式(近战、远程、魔法攻击)。
- 避免多重继承的复杂性:当多个类需要共享不同行为时,策略模式比继承更灵活。
实际案例:电商平台的优惠券系统
问题背景
电商平台的优惠券规则复杂多变,例如:
- 满减券:订单金额满 300 元减 50 元。
- 折扣券:订单金额打 8 折。
- 品类券:指定类目商品 5 折。
若直接在订单计算逻辑中硬编码这些规则,代码将很快变得难以维护。
策略模式解决方案
1. 定义优惠策略接口
public interface DiscountStrategy {
double calculateDiscount(double originalPrice);
}
2. 实现具体策略
// 满减策略
public class FullReductionStrategy implements DiscountStrategy {
private final double threshold;
private final double discount;
public FullReductionStrategy(double threshold, double discount) {
this.threshold = threshold;
this.discount = discount;
}
@Override
public double calculateDiscount(double originalPrice) {
if (originalPrice >= threshold) {
return originalPrice - discount;
}
return originalPrice;
}
}
// 折扣策略
public class PercentageDiscountStrategy implements DiscountStrategy {
private final double percentage;
public PercentageDiscountStrategy(double percentage) {
this.percentage = percentage;
}
@Override
public double calculateDiscount(double originalPrice) {
return originalPrice * (1 - percentage);
}
}
3. 上下文类与客户端调用
public class OrderContext {
private DiscountStrategy discountStrategy;
public void setDiscountStrategy(DiscountStrategy strategy) {
this.discountStrategy = strategy;
}
public double calculateFinalPrice(double originalPrice) {
if (discountStrategy != null) {
return discountStrategy.calculateDiscount(originalPrice);
}
return originalPrice;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
OrderContext order = new OrderContext();
// 根据用户选择的优惠券类型动态设置策略
String couponType = "满减券";
if ("满减券".equals(couponType)) {
order.setDiscountStrategy(new FullReductionStrategy(300, 50));
} else if ("折扣券".equals(couponType)) {
order.setDiscountStrategy(new PercentageDiscountStrategy(0.2));
}
double finalPrice = order.calculateFinalPrice(400);
System.out.println("最终价格:" + finalPrice); // 输出:350.0(满减后)
}
}
策略模式的扩展与注意事项
扩展方向
- 工厂模式结合:通过工厂类根据配置动态生成策略实例,例如从数据库或配置文件加载策略类型。
- 策略链(Chain of Responsibility):当多个策略需按顺序执行时,可结合责任链模式实现。
注意事项
- 避免过度设计:若场景中策略数量极少且固定,使用策略模式可能增加复杂度。
- 策略的上下文依赖:某些策略可能需要访问上下文的内部状态,需通过参数传递或接口暴露必要数据。
- 性能优化:频繁切换策略可能导致对象创建开销,可通过对象池或缓存优化。
结论
策略模式通过封装行为变体、解耦决策逻辑,为复杂条件分支问题提供了一种优雅的解决方案。它不仅提升了代码的可维护性和扩展性,还降低了业务规则变更对系统的冲击。无论是电商优惠计算、游戏AI行为,还是权限控制,策略模式都能帮助开发者构建更灵活、可扩展的系统。
掌握策略模式的核心思想后,开发者可以进一步探索其他设计模式(如观察者模式、装饰器模式),逐步构建出结构清晰、易于协作的高质量代码体系。