一、定义
策略模式定义了一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。本模式使得算法可独立于使用它的客户端而变化。
通俗理解:将一组相似的行为抽象出来,通过统一的接口进行约束,客户端根据实际情况选择使用哪种行为策略。
二、适用场景
策略模式适用于以下场景:
- 系统中有许多类仅在行为上略有不同,使用条件语句(
if-else
或switch-case
)判断行为; - 希望将算法的使用与实现解耦,使代码更加灵活可扩展;
- 需要动态地在运行时切换不同的算法或行为;
- 需要避免类中膨胀的行为逻辑,使得行为扩展对调用者透明。
三、核心角色
策略模式包含以下几个核心角色:
角色 | 说明 |
Context | 上下文类,持有一个策略对象,并调用策略对象的方法来完成任务 |
Strategy(接口) | 抽象策略,定义所有支持的算法的公共接口 |
ConcreteStrategy | 具体策略,实现具体的算法或行为 |
四、UML 类图结构
+----------------+
| Strategy |<-------------------+
| +execute() | |
+----------------+ |
▲ |
| |
+------------------------+ +------------------------+
| ConcreteStrategyA | | ConcreteStrategyB |
| +execute() | | +execute() |
+------------------------+ +------------------------+
▲
|
+---------------+
| Context |
| - strategy |
| +setStrategy() |
| +doSomething() |
+---------------+
五、真实业务场景示例
需求背景:
假设我们有一个在线商城,支持多种促销策略:满减、打折、返现、无优惠等。每种促销策略的计算逻辑不同,我们希望用户下单时能自动选择合适的策略,并方便后续扩展。
1. 抽象策略接口(Strategy 角色)
// 抽象策略角色:定义所有具体策略的统一接口
public interface PromotionStrategy {
void execute();
}
2. 具体策略类(ConcreteStrategy 角色)
// 具体策略类A:满减策略
public class FullReductionStrategy implements PromotionStrategy {
@Override
public void execute() {
System.out.println("执行满减策略:满200减50");
}
}
// 具体策略类B:打折策略
public class DiscountStrategy implements PromotionStrategy {
@Override
public void execute() {
System.out.println("执行打折策略:打8折");
}
}
// 具体策略类C:返现策略
public class CashbackStrategy implements PromotionStrategy {
@Override
public void execute() {
System.out.println("执行返现策略:返现30元");
}
}
// 具体策略类D:无促销策略
public class NoPromotionStrategy implements PromotionStrategy {
@Override
public void execute() {
System.out.println("无促销活动");
}
}
3. 上下文类(Context 角色)
// 上下文角色:用于持有某个策略对象并调用其方法
public class PromotionContext {
private PromotionStrategy strategy;
public PromotionContext(PromotionStrategy strategy) {
this.strategy = strategy;
}
// 可选:运行时切换策略
public void setStrategy(PromotionStrategy strategy) {
this.strategy = strategy;
}
// 执行当前策略
public void executeStrategy() {
if (strategy != null) {
strategy.execute();
} else {
System.out.println("未设置促销策略");
}
}
}
4. 客户端代码
// 客户端角色:负责选择合适的策略并传给上下文
public class PromotionTest {
public static void main(String[] args) {
PromotionContext context;
// 使用满减策略
context = new PromotionContext(new FullReductionStrategy());
context.executeStrategy();
// 使用打折策略
context.setStrategy(new DiscountStrategy());
context.executeStrategy();
// 使用返现策略
context.setStrategy(new CashbackStrategy());
context.executeStrategy();
// 使用无优惠策略
context.setStrategy(new NoPromotionStrategy());
context.executeStrategy();
}
}
六、优缺点分析
✅ 优点
- 扩展性强:新增策略只需实现接口,符合开闭原则;
- 消除冗长判断:避免
if-else
或switch
带来的代码臃肿; - 复用性高:策略类之间相互独立,可复用于不同业务场景;
- 更灵活可维护:行为和上下文解耦,修改行为不影响上下文代码。
❌ 缺点
- 策略类数量增多:每新增一种行为都要创建一个类,可能造成类爆炸;
- 客户端需要了解策略:使用者需要知道有哪些策略才能选择,策略选择逻辑如果不统一,也可能分散;
- 策略切换逻辑可能复杂:若需在运行时动态决定策略,需要额外的工厂或注册机制(如策略工厂+枚举)。
七、策略模式与其他模式对比
模式 | 相似点 | 区别 |
状态模式 | 都是封装行为类,通过上下文调用不同的行为 | 状态模式强调对象状态转换,而策略模式更强调行为选择 |
简单工厂模式 | 可用于策略选择 | 策略模式注重行为抽象与可替换,工厂只是选择实现的一种方式 |
策略 + 工厂 | 工厂封装策略创建逻辑,避免业务方知道具体策略类名 | 是策略模式的一种组合用法 |
八、总结
策略模式是行为型设计模式中非常常用的一种,它通过将行为算法独立封装,提升了系统的扩展性和灵活性,尤其适用于行为变动频繁的业务场景。尽管它带来了类的数量增加,但其带来的结构清晰和维护便利往往更值得。