引言:从现实世界到代码世界的面向对象
在商业策略制定中,企业会根据市场环境选择不同的竞争策略;在军事行动中,指挥官会根据敌情选择不同的战术;在游戏对战中,玩家会根据局势调整作战方式。这种根据情境选择不同行为的模式,在软件设计中同样普遍存在。策略模式(Strategy Pattern)正是为解决这类问题而生的经典设计模式。
想象你正在使用导航软件规划路线。同一个目的地,你可以选择:
最快路线:优先考虑时间
最短距离:不考虑路况,只求距离最短
避开高速:宁愿慢些也要省过路费
经济路线:平衡时间和费用
导航软件如何优雅地实现这些不同的路线计算算法?这就是策略模式大显身手的地方。作为行为型设计模式的代表,策略模式让我们能够定义一系列算法,并将每个算法封装起来,使它们可以相互替换。
一、策略模式解决了什么问题?
先看一个常见的反例:
public class Navigator {public void buildRoute(String routeType) {if ("FASTEST".equals(routeType)) {// 复杂的最快路线算法System.out.println("计算最快路线...考虑实时路况");} else if ("SHORTEST".equals(routeType)) {// 最短距离算法System.out.println("计算最短距离...忽略路况");} else if ("ECONOMIC".equals(routeType)) {// 经济路线算法System.out.println("计算经济路线...平衡时间和费用");}// 每新增一种路线类型,就要修改这里} }
这种实现方式存在几个明显问题:
违反开闭原则:新增算法需要修改原有类
代码臃肿:所有算法堆积在一个类中
难以维护:随着算法增加,条件判断越来越复杂
复用困难:相同算法难以在其他地方复用
策略模式正好可以解决这些问题。
二、策略模式的核心结构
策略模式在GoF《设计模式》中的正式定义:
定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
策略模式包含三个核心角色:
Context(上下文):维护对策略对象的引用,负责将客户端请求委托给当前策略
Strategy(策略接口):定义所有支持算法的公共接口
ConcreteStrategy(具体策略):实现策略接口的具体算法类
1. 基础实现
// 策略接口 interface RouteStrategy {void buildRoute(Point start, Point end); }// 具体策略:最快路线 class FastestRouteStrategy implements RouteStrategy {@Overridepublic void buildRoute(Point start, Point end) {System.out.println("计算最快路线...考虑实时路况");// 实际算法实现} }// 具体策略:最短距离 class ShortestRouteStrategy implements RouteStrategy {@Overridepublic void buildRoute(Point start, Point end) {System.out.println("计算最短距离...忽略路况");// 实际算法实现} }// 上下文 class Navigator {private RouteStrategy strategy;public void setStrategy(RouteStrategy strategy) {this.strategy = strategy;}public void buildRoute(Point start, Point end) {if (strategy != null) {strategy.buildRoute(start, end);} else {System.out.println("请先设置路线策略");}} }// 使用示例 public class Client {public static void main(String[] args) {Navigator navigator = new Navigator();// 使用最快路线策略navigator.setStrategy(new FastestRouteStrategy());navigator.buildRoute(pointA, pointB);// 切换为最短距离策略navigator.setStrategy(new ShortestRouteStrategy());navigator.buildRoute(pointA, pointB);} }
三、Spring中的策略模式实践
在Spring应用中,我们可以利用DI(依赖注入)更加优雅地实现策略模式:
// 策略接口 public interface DiscountStrategy {BigDecimal applyDiscount(BigDecimal amount);String getStrategyName(); }// 具体策略:会员折扣 @Service public class MemberDiscountStrategy implements DiscountStrategy {@Overridepublic BigDecimal applyDiscount(BigDecimal amount) {return amount.multiply(new BigDecimal("0.9"));}@Overridepublic String getStrategyName() {return "MEMBER_DISCOUNT";} }// 具体策略:节日折扣 @Service public class FestivalDiscountStrategy implements DiscountStrategy {@Overridepublic BigDecimal applyDiscount(BigDecimal amount) {return amount.multiply(new BigDecimal("0.8"));}@Overridepublic String getStrategyName() {return "FESTIVAL_DISCOUNT";} }// 策略上下文 @Service public class DiscountContext {private final Map<String, DiscountStrategy> strategyMap;@Autowiredpublic DiscountContext(List<DiscountStrategy> strategies) {this.strategyMap = strategies.stream().collect(Collectors.toMap(DiscountStrategy::getStrategyName,Function.identity()));}public BigDecimal applyDiscount(String strategyName, BigDecimal amount) {DiscountStrategy strategy = strategyMap.get(strategyName);if (strategy == null) {throw new IllegalArgumentException("未知的折扣策略");}return strategy.applyDiscount(amount);} }// 控制器使用 @RestController @RequestMapping("/order") public class OrderController {@Autowiredprivate DiscountContext discountContext;@PostMapping("/checkout")public ResponseEntity<BigDecimal> checkout(@RequestParam String discountType,@RequestParam BigDecimal amount) {BigDecimal finalAmount = discountContext.applyDiscount(discountType, amount);return ResponseEntity.ok(finalAmount);} }
Spring策略模式优势:
自动收集所有策略实现
策略与上下文完全解耦
方便扩展新策略
策略可以享受Spring的所有特性(AOP、依赖注入等)
四、策略模式的性能考量
4.1 策略对象的创建成本
对于高频调用的策略,需要考虑策略对象的创建方式:
每次创建新实例:简单但可能产生GC压力
策略对象复用:适合无状态的策略
对象池技术:适用于创建成本高的策略
4.2 策略选择的效率
策略选择方式 | 时间复杂度 | 适用场景 |
---|---|---|
if-else/switch | O(n) | 策略数量少(5个以下) |
Map查找 | O(1) | 策略数量多 |
策略链 | O(n) | 需要依次尝试策略 |
五、策略模式的优缺点分析
优势
开闭原则:无需修改已有代码即可新增策略
消除条件语句:替代大量的if-else或switch-case
提高可测试性:每个策略可以独立测试
运行时灵活性:支持动态切换算法
代码复用:不同上下文可以共享策略
局限性
客户端必须了解策略:需要知道不同策略的区别
策略类增多:可能增加系统中类的数量
通信开销:策略与上下文可能需要交换数据
不适合简单算法:可能会过度设计简单场景
六、策略模式与其他模式的关系
6.1与工厂模式的区别
模式 | 关注点 | 应用阶段 | 主要作用 |
---|---|---|---|
工厂模式 | 对象创建 | 初始化阶段 | 隐藏创建逻辑 |
策略模式 | 行为算法 | 运行时 | 封装可互换的行为 |
6.2 与状态模式的对比
相似点:都有上下文和多个实现类
不同点:
状态模式:状态转换通常由上下文内部管理
策略模式:策略选择通常由客户端控制
最后,策略模式不仅仅是一种编码技巧,它体现了"分而治之"的古老智慧。通过将复杂多变的行为分解为独立的策略单元,我们获得了:
清晰的边界:每个策略只关注自己的算法
灵活的组合:可以像乐高积木一样搭配策略
可控的变化:算法变化被隔离在策略内部
正如《孙子兵法》所言:"兵无常势,水无常形",软件设计也应该能够随需而变。策略模式正是帮助我们实现这种灵活性的利器。
设计模式的最高境界是"无招胜有招"——不是机械地套用模式,而是理解其思想后自然地融入设计中。