策略模式(Strategy Pattern)详解
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列算法,并将每种算法封装到独立的策略类中,使它们可以相互替换,从而使算法的变化独立于使用算法的客户端(即使用这些算法的代码)。通过策略模式,用户可以根据需要选择不同的算法,实现系统的 开放-关闭原则(Open/Closed Principle)。
1. 策略模式的定义
1.1 什么是策略模式?
策略模式定义了一系列算法,将每个算法封装起来,并且使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化。它的核心思想是:定义一组可互换的策略对象,将这些策略对象注入到上下文对象中,从而让上下文对象在运行时可以动态地更换不同的策略。
1.2 策略模式的特点
- 封装变化:将算法封装在独立的类中,使得算法的修改不会影响使用算法的客户端。
- 消除条件语句 :避免在客户端代码中使用大量的
if-else
或switch-case
语句。 - 提高扩展性:新增策略时,只需添加新的策略类,而无需修改现有代码。
2. 策略模式的结构
策略模式通常包含以下几个角色:
- 策略接口(Strategy) :
- 定义所有支持的算法的公共接口。
- 具体策略(ConcreteStrategy) :
- 实现策略接口的具体算法。
- 上下文(Context) :
- 持有策略接口的引用,用于调用具体的策略方法。
类图
Lua
+-----------------+
| Strategy |<---------------------+
|-----------------| |
| + algorithm() | |
+-----------------+ |
^ |
| |
+-----------------+ +-------------------+
| ConcreteStrategyA | | ConcreteStrategyB |
|-------------------| |--------------------|
| + algorithm() | | + algorithm() |
+-------------------+ +--------------------+
^
|
+---------------------+
| Context |
|---------------------|
| - strategy: Strategy|
| + setStrategy() |
| + executeAlgorithm()|
+---------------------+
3. 策略模式的实现
为了更好地理解策略模式,我们使用一个简单的示例来演示其工作原理。假设我们要开发一个应用程序,用于计算商品的促销折扣。不同的促销活动有不同的折扣计算策略。
3.1 Java 示例代码
Lua
// 策略接口
interface DiscountStrategy {
double calculateDiscount(double price);
}
// 具体策略类:圣诞节促销
class ChristmasDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
System.out.println("使用圣诞节促销折扣");
return price * 0.9; // 10% 折扣
}
}
// 具体策略类:新年促销
class NewYearDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
System.out.println("使用新年促销折扣");
return price * 0.8; // 20% 折扣
}
}
// 具体策略类:无折扣
class NoDiscount implements DiscountStrategy {
@Override
public double calculateDiscount(double price) {
System.out.println("没有折扣");
return price;
}
}
// 上下文类
class ShoppingCart {
private DiscountStrategy discountStrategy;
// 设置策略
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 计算最终价格
public double calculateFinalPrice(double price) {
return discountStrategy.calculateDiscount(price);
}
}
// 测试客户端
public class StrategyPatternDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 使用圣诞节折扣
cart.setDiscountStrategy(new ChristmasDiscount());
System.out.println("最终价格: " + cart.calculateFinalPrice(100.0));
// 使用新年折扣
cart.setDiscountStrategy(new NewYearDiscount());
System.out.println("最终价格: " + cart.calculateFinalPrice(100.0));
// 使用无折扣
cart.setDiscountStrategy(new NoDiscount());
System.out.println("最终价格: " + cart.calculateFinalPrice(100.0));
}
}
输出结果:
Lua
使用圣诞节促销折扣
最终价格: 90.0
使用新年促销折扣
最终价格: 80.0
没有折扣
最终价格: 100.0
4. 策略模式的应用场景
策略模式适合以下场景:
- 多个类只在行为上有所不同 :
- 当多个类的功能类似,仅在算法或行为上有所区别时,可以使用策略模式来封装这些行为。
- 消除条件语句 :
- 当系统中有大量的
if-else
或switch-case
语句时,可以考虑使用策略模式来替代这些条件语句。
- 当系统中有大量的
- 需要动态更改算法 :
- 例如支付方式(信用卡、PayPal、比特币)、排序算法(快速排序、归并排序)、日志记录方式(文件、数据库、控制台)。
- 系统需要灵活扩展 :
- 可以在不修改原有代码的情况下,轻松添加新的策略。
5. 策略模式的优缺点
5.1 优点
- 符合开闭原则:可以在不修改原有代码的情况下扩展新的策略。
- 避免使用复杂的条件判断:通过策略的多态性来消除条件语句,使代码更清晰、更易维护。
- 提高代码的复用性:将每个策略封装到独立的类中,使其可以被多个上下文重用。
5.2 缺点
- 增加类的数量:每个策略都需要定义一个类,导致类的数量增加,可能会使系统变得复杂。
- 客户端必须知道所有的策略:客户端需要了解所有可用的策略,以便在运行时选择合适的策略。
- 策略之间无法共享数据:策略类是相互独立的,无法直接共享数据,可能导致重复代码。
6. 策略模式的扩展
6.1 结合工厂模式(Factory Pattern)
策略模式可以与工厂模式结合使用,通过工厂类动态创建策略对象,减少客户端对策略类的直接依赖。
6.2 结合依赖注入(Dependency Injection)
在实际开发中,策略模式常与依赖注入结合使用,将策略对象通过构造函数或方法参数注入到上下文中。
6.3 Java 8 Lambda 表达式的简化
在 Java 8 中,可以使用 Lambda 表达式来简化策略模式的实现:
java
import java.util.function.Function;
public class StrategyPatternWithLambda {
public static void main(String[] args) {
Function<Double, Double> christmasDiscount = price -> price * 0.9;
Function<Double, Double> newYearDiscount = price -> price * 0.8;
Function<Double, Double> noDiscount = price -> price;
double price = 100.0;
System.out.println("圣诞节折扣: " + christmasDiscount.apply(price));
System.out.println("新年折扣: " + newYearDiscount.apply(price));
System.out.println("无折扣: " + noDiscount.apply(price));
}
}
7. 策略模式的实际应用示例
- 支付系统 :
- 支持多种支付方式(如支付宝、微信、信用卡、PayPal),用户可以根据需要选择不同的支付方式。
- 日志记录系统 :
- 可以将日志记录到文件、数据库或控制台等不同的地方,日志策略可以根据配置动态切换。
- 数据压缩算法 :
- 支持不同的数据压缩算法(如 ZIP、RAR、GZIP),用户可以选择不同的压缩策略。
8. 总结
策略模式是一个非常有用的设计模式,尤其是在需要根据不同的条件选择不同的算法时。通过将算法封装成独立的类,并将这些类抽象化为策略接口,可以有效地提高代码的复用性、可维护性和扩展性。
- 优点:符合开闭原则、消除条件语句、提高代码复用性。
- 缺点:增加类数量、客户端需要了解策略、策略间无法共享数据。
- 适用场景:算法选择、支付方式、日志记录、多样化业务逻辑。