策略接口必须用interface而非abstract class,因其强制实现类仅暴露行为契约、避免冗余继承;Context应通过构造注入final策略实例,禁用setter;Spring中用自定义@Qualifier注解区分多策略实现;策略类须通过参数接收必要数据,禁止访问Context私有字段。

策略接口定义必须用 interface 而不是 abstract class
策略模式的核心是「算法可互换」,这要求所有具体策略必须能被统一调用。用 interface 定义 Strategy 是最稳妥的选择——它强制实现类只暴露行为契约,不携带状态或默认逻辑,避免子类误继承冗余字段或方法。
常见错误是用 abstract class 定义策略基类,结果导致:
• 具体策略被迫继承无用的父类字段
• 后续想组合多个策略时无法多继承
• 单元测试时容易因父类构造逻辑失败
正确做法:
public interface DiscountStrategy {
double calculate(double originalPrice);
}
Context 类不应持有策略实例的 setter 方法
Context 的职责是「委托执行」,不是「动态切换策略的容器」。暴露 setStrategy(…) 方法看似灵活,实则破坏封装,让外部可以随意篡改内部行为逻辑,尤其在并发场景下极易引发状态不一致。
立即学习“Java免费学习笔记(深入)”;
更合理的做法是:策略由 Context 构造时注入,且不可变。
- 构造器参数接收
DiscountStrategy,并用final修饰成员变量 - 避免提供 public setter
- 如需运行时切换,应通过工厂或上下文重建,而非修改已有实例
示例:
public class OrderProcessor {
private final DiscountStrategy strategy;
public OrderProcessor(DiscountStrategy strategy) {
this.strategy = strategy; // final,不可重置
}
public double process(double price) {
return strategy.calculate(price);
}
}
Spring 中用 @Qualifier 区分多个策略实现
当项目中存在多个 DiscountStrategy 实现(如 MemberDiscountStrategy、VipDiscountStrategy),直接 @Autowired 会报 NoUniqueBeanDefinitionException。不能靠类型自动装配,必须显式指定。
推荐方案是结合 @Qualifier 和自定义注解,比字符串字面量更安全:
- 定义
@interface DiscountType { String value(); } - 为每个实现类标注
@DiscountType("vip")等 - 在注入点使用
@Autowired @DiscountType("vip") DiscountStrategy strategy
这样既避免硬编码字符串,又支持编译期校验,IDE 也能跳转到对应实现。
策略类不应访问 Context 的私有字段
策略实现里出现 ((OrderContext) context).getCustomer().getLevel() 这类强转+反射式访问,说明设计已偏离策略模式本意。策略应该只依赖输入参数,而不是反向扒 Context 的内部结构。
正确做法是:Context 在调用前把策略所需信息全部作为参数传入。例如:
- 错:
strategy.apply(context)—— 把整个 Context 塞进去 - 对:
strategy.calculate(price, customerLevel, orderDate)—— 显式传递必要上下文数据
这样做能让策略真正「可独立测试」,也避免策略类随 Context 内部重构而频繁改动。
策略模式真正的难点不在结构搭建,而在划清「谁该知道什么」——一旦策略开始窥探 Context 的私有实现,就等于把算法和上下文耦合回去了。










