Java中的策略模式
综述
本文总结了策略模式的定义,特点,使用场景以及实现思路。
策略模式的定义
策略模式说通了, 就是定义一系列的算法, 将它们各自封装起来, 并且使用一个共同的接口使它们可相互替换. 使得算法和算法之间没有耦合, 这样如果方法需要修改或者添加, 工程师不需要修改那些无关的算法. 特别是当业务逻辑需要从多种算法之中挑选自己需要的算法时, 采用策略模式会非常的有效.
策略模式的特点和使用场合
策略模式主要使用场景在于将业务代码和具体算法进行解耦. 当某个业务代码需要用到某种算法来实现这个功能, 且不能保证未来一直永这类算法, 并共用同一个接口, 这时候就可以考虑策略模式. 常见的使用场景包括排序算法、缓存策略、支付方式等等
策略模式有以下优点:
- 对算法或行为进行封装, 方便开发和理解.
- 切换方便, 扩展也方便.
当然策略模式也有缺点, 策略模式为了将算法和业务模式解耦, 额外添加了类的数量以及代码量. 所以在算法数量较少的时候(特别是干脆只有一种算法的时候)不建议采用策略模式, 避免出现杀鸡用牛刀空耗时间与精力.
策略模式的实现思路
策略模式包含以下几个角色:
- 上下文(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。上下文类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
- 抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
- 具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
如果读者熟悉状态模式的话就会发现二者非常的相似, 因为状态模式的核心角色分别是 上下文 抽象状态和具体状态. 可以发现这两个模式在很多地方有类似之处, 这也是为什么很多开发者会选择用策略模式替代状态模式的原因. 但是两者仍然存在一些本质上的不同.
对于状态模式而言, 状态的切换应该是固定的, 切换顺序根据不同的实现思路应该内置于具体状态或者上下文之中. 而策略模式中不同的状态切换是外置的, 是可以交由context的实例的调用者根据需要以任意切换的.
策略模式示例
下面展示一个有足够泛用性示例, 策略模式同时还结合了工厂模式, 实际使用的时候还可以结合单例模式但是此处就不再继续拓展了
首先是 AbstractStrategy 接口, 这里内部的方法叫什么不重要, 重要的是所有策略都需要实现同样的方法, 既所有的策略必须接收同样的参数, 或者不接收任何参数, 当然如果不同的策略实际用到的参数不一样, 可以使用最大集构建一个接收所有参数的execute(para...) 方法, 这样用到哪个参数就调用哪个参数, 用不到就忽略
public interface AbstractStrategy {
void execute();
}
然后是 AbstractStrategy 的各种实现类, 既单独的策略
public class FooBarConcreteStrategy implements AbstractStrategy{
@Override
public void execute() {
//在真实的使用时可以在方法中进行任意操作,
//此处只使用打印文字作为举例, 下同
System.out.println("foobar");
}
}
public class HelloWorldConcreteStrategy implements AbstractStrategy{
@Override
public void execute() {
System.out.println("hello, world");
}
}
然后是context类
public class ExecuteContext {
private AbstractStrategy strategy;
// 设置策略
public void setStrategy(AbstractStrategy strategy) {
this.strategy = strategy;
}
// 执行策略
public void executeStrategy() {
strategy.execute();
return;
}
}
最后是一个调用的示例, 实际使用的时候当然要放在具体的方法中
public class StrategyPattern {
public static void main(String[] args){
ExecuteContext context = new ExecuteContext();
context.setStrategy(new HelloWorldConcreteStrategy());
context.executeStrategy();
}
}
策略模式拓展版本
因为纯策略模式要求调用者熟悉当前需要测策略, 在易用性方面不够友好, 所以这里给出一个策略模式+简单工厂模式的拓展版本
相较于原来的策略模式, 修改context, 这里context同时还承担了factory的职责, 所以核心是STRATEGY_MAP, 用于对外返回需要的策略实例. 同时内置一个public类型的内部枚举类, 这样外部调用策略实例的时候可以通过枚举类的提示知道当前提供哪几种策略. 具体如下
public class ExecuteContextExtension {
private static final Map<ExecuteStrategyName, AbstractStrategy> STRATEGY_MAP = new HashMap<>();
static {
STRATEGY_MAP.put(ExecuteStrategyName.HelloWorld, new HelloWorldConcreteStrategy());
STRATEGY_MAP.put(ExecuteStrategyName.FooBar, new FooBarConcreteStrategy());
}
public AbstractStrategy getStrategy(ExecuteStrategyName name){
return STRATEGY_MAP.get(name);
}
public enum ExecuteStrategyName {
HelloWorld,
FooBar,
}
}
调用的写法为
public class StrategyPattern {
public static void main(String[] args){
ExecuteContextExtension factory = new ExecuteContextExtension();
AbstractStrategy foobarStrategy = factory.getStrategy(ExecuteStrategyName.FooBar);
foobarStrategy.execute();
}
其余不变
这一拓展的好处在于, 对于调用者而言它可以直接从context内置的枚举类中知道整个模块提供了哪几种策略. 易用性拉满, 当然缺点是对开闭原则的支持没有纯策略模式那么好.
总结
策略模式是23种设计模式中的一种, 属于行为模式这一细分类别, 主要用于解耦业务逻辑和具体算法. 个人以为, 策略模式属于和观察者模式以及工厂模式同一级别的标杆级设计模式, 构思精妙, 威力强大, 应用广泛. 当然和单例模式这种简单易行的模式不同, 也就没有速成之法, 新手从接触到上手总还是要半天时间.
标签:Java,策略,模式,算法,AbstractStrategy,context,public From: https://www.cnblogs.com/dwcg/p/18175381