策略模式:算法族与可替换行为
FreeGuideOnline
最新
2026-06-18
策略模式:算法族与可替换行为
策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装到具有共同接口的独立类中,并使它们可以相互替换。该模式让算法的变化独立于使用算法的客户端,从而在运行时动态切换行为,避免冗长的条件判断。
为什么需要策略模式
在开发中经常会遇到“根据条件执行不同行为”的需求。最直接的做法是使用 if-else 或 switch-case 进行硬编码,但随着条件分支增多,代码会变得臃肿、难以维护和扩展。策略模式通过将每个分支逻辑抽取为独立的策略类,解决了以下问题:
- 消除复杂的条件判断语句,提高可读性。
- 增加新策略时无需修改现有客户端代码,符合开闭原则。
- 各策略类可以独立测试、复用,降低了耦合度。
核心结构
策略模式通常包含三个角色:
- 策略接口(Strategy):定义所有策略类必须实现的公共方法,通常是一个行为抽象。
- 具体策略(Concrete Strategy):实现策略接口的具体算法类。
- 上下文(Context):持有一个策略接口的引用,负责将客户端的调用委派给当前策略对象。
+----------------+ +---------------------+
| Context | | <<interface>> |
|----------------| | Strategy |
| - strategy: Strategy |---------------------|
| + setStrategy(Strategy) | + execute() |
| + executeStrategy() | |
+----------------+ +---------------------+
| ^
| |
| +-----------+-----------+
| | |
| +-----------------+ +-----------------+
+----------->| ConcreteStrategyA| | ConcreteStrategyB|
|-----------------| |-----------------|
| + execute() | | + execute() |
+-----------------+ +-----------------+
策略模式的实现步骤
- 识别变化部分:将容易发生变化的行为(算法、规则、业务逻辑)抽象出来。
- 定义策略接口:创建一个接口,声明所有可变行为需要遵循的公共方法。
- 实现具体策略:将每种变化封装为独立类,实现策略接口。
- 构建上下文类:在上下文类中组合策略接口,并提供方法供客户端动态切换策略。
- 客户端调用:客户端根据具体场景选择并设置策略,然后通过上下文执行行为。
代码演示(Java 示例)
假设我们有一个支付系统,支持多种支付方式(支付宝、微信支付、银行卡)。使用策略模式可以这样设计:
步骤1:定义支付策略接口
public interface PaymentStrategy {
void pay(double amount);
}
步骤2:实现具体策略类
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
public class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
public class CardPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用银行卡支付 " + amount + " 元");
}
}
步骤3:定义上下文订单类
public class Order {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(double amount) {
if (paymentStrategy == null) {
throw new IllegalStateException("请先选择支付方式");
}
paymentStrategy.pay(amount);
}
}
步骤4:客户端使用
public class Client {
public static void main(String[] args) {
Order order = new Order();
// 使用支付宝支付
order.setPaymentStrategy(new AlipayStrategy());
order.checkout(100.0);
// 动态切换为微信支付
order.setPaymentStrategy(new WechatPayStrategy());
order.checkout(200.0);
}
}
通过上述设计,新增一种支付方式时,只需要增加一个新的策略类并实现 PaymentStrategy 接口,无需改动 Order 类或其他已有代码。
实战场景:电商促销折扣计算
在电商系统中,不同用户等级享受不同的折扣策略(普通用户无折扣、VIP 用户 9 折、SVIP 用户 8 折)。传统做法可能写满 if 判断,但使用策略模式可以让促销逻辑更优雅:
- 定义
DiscountStrategy接口,包含calculatePrice(originalPrice)方法。 - 实现
NormalDiscount、VipDiscount、SvipDiscount。 - 在
ShoppingCart上下文中组合DiscountStrategy,结算时动态选择策略。
这样,当需要新增“节日闪购 5 折”策略时,只需新建一个类即可,完全符合开闭原则。
策略模式与其它设计模式的结合
- 工厂模式:通常与策略模式结合,由工厂负责根据参数创建对应的策略对象,进一步降低客户端对具体策略类的依赖。
- 单例模式:如果策略类本身无状态,可以设计为单例,避免反复创建对象。
- 模板方法模式:可以在策略接口的骨架中定义算法步骤,让具体策略实现部分细节。
- 状态模式:二者结构相似,但策略模式是让客户端主动切换算法,状态模式则是对象内部状态改变时自动切换行为。
优缺点一览
优点:
- 避免多重条件判断,代码清晰。
- 符合开闭原则,新增策略无需修改已有代码。
- 策略类可以独立测试、复用,提升可维护性。
- 运行时灵活切换算法,增强系统扩展性。
缺点:
- 增加了类的数量,每个策略都是一个类,可能造成类膨胀。
- 客户端必须了解各个策略之间的差异,以便选择合适的策略。
- 策略类之间无法共享数据,除非通过上下文传递公共参数。
最佳实践与注意事项
- 策略无状态化:尽量让策略对象不持有状态,确保线程安全,并可复用。
- 使用函数式接口:在 Java 8+ 中,若策略接口只有一个方法,可以直接用 Lambda 表达式代替具体策略类,进一步简化代码。
- 避免过度设计:只有当一个行为有至少两三种变体时才考虑策略模式,否则简单的
if-else可能更直观。 - 结合配置化:将策略的选择逻辑配置在配置文件或数据库中,可以在不重新发布的情况下动态改变行为。
策略模式的核心思想是“封装变化”,将行为抽象为可互换的组件。合理使用该模式可以使系统更加灵活、可扩展,是应对复杂业务规则变化的有力工具。