策略模式:动态切换算法的设计智慧
一、模式核心:定义一系列算法并可相互替换
在软件开发中,常常会遇到需要根据不同情况选择不同算法的场景。例如,在电商系统中,根据不同的促销活动(如满减、折扣、赠品)来计算商品的最终价格。
策略模式(Strategy Pattern) 定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端,核心解决:
- 算法切换:在运行时根据不同条件动态选择合适的算法。
- 代码复用:将不同算法封装成独立的策略类,提高代码的复用性。
- 可维护性:算法的修改和扩展不会影响到使用算法的客户端。
核心思想与 UML 类图
策略模式包含策略接口(Strategy)、具体策略类(Concrete Strategy)和上下文类(Context)。上下文类持有一个策略接口的引用,客户端可以在运行时动态设置上下文类所使用的具体策略。

二、核心实现:电商促销策略
1. 定义策略接口(促销策略)
java
public interface PromotionStrategy {
double calculatePrice(double originalPrice); // 计算促销后的价格
}
2. 实现具体策略类
满减策略
java
public class FullReductionStrategy implements PromotionStrategy {
private double fullAmount;
private double reductionAmount;
public FullReductionStrategy(double fullAmount, double reductionAmount) {
this.fullAmount = fullAmount;
this.reductionAmount = reductionAmount;
}
@Override
public double calculatePrice(double originalPrice) {
if (originalPrice >= fullAmount) {
return originalPrice - reductionAmount;
}
return originalPrice;
}
}
折扣策略
java
public class DiscountStrategy implements PromotionStrategy {
private double discountRate;
public DiscountStrategy(double discountRate) {
this.discountRate = discountRate;
}
@Override
public double calculatePrice(double originalPrice) {
return originalPrice * discountRate;
}
}
无促销策略
java
public class NoPromotionStrategy implements PromotionStrategy {
@Override
public double calculatePrice(double originalPrice) {
return originalPrice;
}
}
3. 实现上下文类(购物车)
java
public class ShoppingCart {
private PromotionStrategy promotionStrategy;
public void setPromotionStrategy(PromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
public double checkout(double originalPrice) {
if (promotionStrategy == null) {
promotionStrategy = new NoPromotionStrategy();
}
return promotionStrategy.calculatePrice(originalPrice);
}
}
4. 客户端使用策略模式
java
public class ClientDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 无促销活动
cart.setPromotionStrategy(new NoPromotionStrategy());
double price1 = cart.checkout(100);
System.out.println("无促销活动,最终价格:" + price1);
// 满 100 减 20
cart.setPromotionStrategy(new FullReductionStrategy(100, 20));
double price2 = cart.checkout(120);
System.out.println("满 100 减 20,最终价格:" + price2);
// 打 8 折
cart.setPromotionStrategy(new DiscountStrategy(0.8));
double price3 = cart.checkout(150);
System.out.println("打 8 折,最终价格:" + price3);
}
}
输出结果:
plaintext
无促销活动,最终价格:100.0
满 100 减 20,最终价格:100.0
打 8 折,最终价格:120.0
三、进阶:策略模式与工厂模式结合
在实际应用中,为了更方便地管理和获取策略对象,可以将策略模式与工厂模式结合。
java
public class PromotionStrategyFactory {
public static PromotionStrategy getStrategy(String strategyType) {
switch (strategyType) {
case "full_reduction":
return new FullReductionStrategy(100, 20);
case "discount":
return new DiscountStrategy(0.8);
default:
return new NoPromotionStrategy();
}
}
}
public class AdvancedClientDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 使用工厂获取策略
cart.setPromotionStrategy(PromotionStrategyFactory.getStrategy("full_reduction"));
double price = cart.checkout(120);
System.out.println("使用工厂获取策略,最终价格:" + price);
}
}
四、框架与源码中的策略模式实践
1. Java 的 Comparator 接口
在 Java 中,Comparator
接口就是策略模式的典型应用。通过实现不同的 Comparator
接口,可以为不同的排序需求提供不同的排序策略。
java
import java.util.Arrays;
import java.util.Comparator;
public class ComparatorDemo {
public static void main(String[] args) {
Integer[] numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
// 升序排序
Arrays.sort(numbers, Comparator.naturalOrder());
System.out.println("升序排序:" + Arrays.toString(numbers));
// 降序排序
Arrays.sort(numbers, Comparator.reverseOrder());
System.out.println("降序排序:" + Arrays.toString(numbers));
}
}
2. Spring 的 ResourceLoader
Spring 框架中的 ResourceLoader
接口及其实现类也是策略模式的应用。不同的 ResourceLoader
实现类可以根据不同的资源类型(如文件、类路径资源、URL 资源等)提供不同的资源加载策略。
java
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringResourceLoaderDemo {
public static void main(String[] args) {
ResourceLoader resourceLoader = new ClassPathXmlApplicationContext();
Resource resource = resourceLoader.getResource("application.properties");
System.out.println("资源是否存在:" + resource.exists());
}
}
五、避坑指南:正确使用策略模式的 3 个要点
1. 避免策略类过多
当策略类过多时,会导致类的数量急剧增加,增加系统的复杂度。可以考虑将一些相似的策略类进行合并,或者使用策略枚举来简化策略的管理。
2. 策略的选择逻辑
在使用策略模式时,需要考虑如何选择合适的策略。可以将策略的选择逻辑封装在上下文类中,或者使用工厂模式来管理策略的创建和选择。
3. 策略的可维护性
每个策略类都应该保持独立和单一职责,避免在策略类中添加过多的业务逻辑。同时,要为策略类提供清晰的文档和注释,方便后续的维护和扩展。
六、总结:何时该用策略模式?
适用场景 | 核心特征 | 典型案例 |
---|---|---|
算法的动态切换 | 需要根据不同条件在运行时动态选择算法 | 电商促销、排序算法选择 |
代码复用和可维护性 | 多个算法具有相似的接口,需要提高代码的复用性和可维护性 | 图形绘制算法、加密算法 |
避免使用大量条件语句 | 避免在代码中使用大量的 if-else 或 switch 语句 |
游戏中的角色技能、状态机 |
策略模式通过将算法封装成独立的策略类,实现了算法的动态切换和代码的复用,是一种非常实用的设计模式。下一篇我们将深入探讨模板方法模式,解析如何定义算法骨架并延迟实现细节,敬请期待!
扩展思考:策略模式 vs 状态模式
类型 | 核心思想 | 适用场景 |
---|---|---|
策略模式 | 定义一系列算法并可相互替换,客户端主动选择策略 | 算法的动态切换、代码复用 |
状态模式 | 对象的行为依赖于其状态,状态的改变会导致行为的改变 | 对象的行为随状态变化、状态机 |
理解这种差异,能帮助我们在不同场景下选择更合适的设计模式。