在Java里如何实现策略模式_JavaStrategyPattern面向对象应用说明

策略接口必须用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(…) 方法看似灵活,实则破坏封装,让外部可以随意篡改内部行为逻辑,尤其在并发场景下极易引发状态不一致。

更合理的做法是:策略由 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 实现(如 MemberDiscountStrategyVipDiscountStrategy),直接 @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 的私有实现,就等于把算法和上下文耦合回去了。