Design-Pattern-Strategy
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法(策略),并将每个算法封装起来,使它们可以互相替换,且不影响客户端的使用。
场景
在日常生活中,做一件事通常有多种方式。例如点外卖时,可以选择距离优先,好评优先对商家进行排序。从代码开发角度,如果存在多种排序规则,则可以定义一个排序方法,接受排序规则作为入参,一段典型的代码如下:
1 | |
但后续需要新增或修改排序规则时,就需要修改上述方法,这不符合开闭原则。并且难以维护,所有逻辑混在一起,逻辑复杂时难以阅读。
策略模式
既然已经想到可以将排序规则字符串作为入参传入,那么很容易进一步联想到:也可以直接把算法本身作为入参传入。在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函数式接口和Collections的sort方法。再进一步,可以用lambda表达式替换匿名实现类。
从结构与使用方式上看,策略模式与状态模式高度相似。语义层面而言,不同状态下的行为逻辑,本质上可视作策略的具体表现。二者最核心的差异在于:状态模式中,状态对象需感知其他状态的存在,并可能在执行逻辑时主动切换至其他状态;而策略模式里的策略对象彼此独立、互不感知。
总结
策略模式定义策略接口,使得上下文可以动态切换不同的具体策略。
在java中,若策略仅在局部使用,可以通过函数式接口、lambda表达式替换简化。