Design-Pattern-Strategy

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法(策略),并将每个算法封装起来,使它们可以互相替换,且不影响客户端的使用

场景

在日常生活中,做一件事通常有多种方式。例如点外卖时,可以选择距离优先,好评优先对商家进行排序。从代码开发角度,如果存在多种排序规则,则可以定义一个排序方法,接受排序规则作为入参,一段典型的代码如下:

1
2
3
4
5
6
7
8
9
10
public List<Merchant> merchant_sort(List<Merchant> merchants, String strategy) {
if ("FASTEST".equals(strategy)) {
// 最快配送排序
} else if ("RATING".equals(strategy)) {
// 商家评分排序
} else if ("RANKING".equals(strategy)) {
// 销量排序
}
....
}

但后续需要新增或修改排序规则时,就需要修改上述方法,这不符合开闭原则。并且难以维护,所有逻辑混在一起,逻辑复杂时难以阅读。

策略模式

既然已经想到可以将排序规则字符串作为入参传入,那么很容易进一步联想到:也可以直接把算法本身作为入参传入。在C语言中,这种将方法作为参数传入的特性被称为可变方法,其本质是通过函数指针来实现动态调用不同的函数。而在Java中,由于万事万物皆对象的特性,方法无法单独存在,必须依托对象作为载体进行传入。为了让调用方能够灵活切换不同的方法,自然需要先定义一个策略接口,每当新增一个算法时,就对应新增一个实现该接口的具体类。

至此已将策略模式中的角色介绍完毕,十分简洁的模式结构。规范下术语,定义不同算法签名的接口称为策略接口;其具体实现类称为具体策略;调用策略的类称为上下文;客户端通过将具体策略传递给上下文,来达到运行时切换上下类实现某功能的算法。各个角色的关系类图如下。

classDiagram
    direction LR
    class Context {
        -strategy: Strategy
        +setStrategy(strategy: Strategy)
        +method()
    }

    class Strategy {
        <<interface>>
        +execute(data: Data)
    }

    class ConcreteStrategy {
        +execute(data: Data)
    }
    
    class Client {
    }

    Context --o Strategy
    Strategy <|-- ConcreteStrategy
    Client --> Context
    Client --> ConcreteStrategy

上下文可以通过成员变量引用策略,也可以通过方法参数切换不同具体策略,均是可以的。类图中使用的是通过成员变量绑定了具体策略。

若策略接口仅声明一个方法,它便成为Java中的函数式接口。对于那些以函数式接口作为参数的方法,若策略仅在局部使用、不会在多处被重复调用,甚至无需显式定义策略实现类——可直接在调用上下文方法时,通过匿名实现类快速实现策略逻辑。例如JDK中的Comparator函数式接口和Collectionssort方法。再进一步,可以用lambda表达式替换匿名实现类。

从结构与使用方式上看,策略模式与状态模式高度相似。语义层面而言,不同状态下的行为逻辑,本质上可视作策略的具体表现。二者最核心的差异在于:状态模式中,状态对象需感知其他状态的存在,并可能在执行逻辑时主动切换至其他状态;而策略模式里的策略对象彼此独立、互不感知。

总结

策略模式定义策略接口,使得上下文可以动态切换不同的具体策略。

java中,若策略仅在局部使用,可以通过函数式接口、lambda表达式替换简化。


Design-Pattern-Strategy
http://dracoyus.github.io/2025/11/19/Design-Pattern-Strategy/
作者
DracoYu
发布于
2025年11月19日
许可协议