前言
各位帅哥美女编码的时候有没有下面这种情况?
或者是
繁杂的条件,几十上百种情况是不是让人上火?
这个时候就可以考虑使用策略模式简化上面这两种情况了。
策略模式
定义
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。(内容来自百度百科)
个人理解
策略模式重点是如何组织调度算法,而不在于编写算法。
策略模式下各种算法是平等的相互没有依赖的可替换的。
一次抉择只可以使用一种策略。
适用场景
场景其实就是我上面两张图,业务需要根据不同场景切换不同的实现逻辑时
1.替换大量冗长的ifelse
2.替换大量的switch
策略模式的优点
1、策略模式其实就是提供管理相关的算法集的办法。策略类可以封装一种算法或行为,那么当某个策略发生变化的时候,在修改代码时,其他策略不受影响。在新增策略的时候不用修改已有代码,符合开闭原则。
2、使用策略模式可以避免较多的ifelse和switch。
策略模式的缺点
1.客户端必须知道所有的策略,并且自己决定使用哪种策略。客户端需要了解对应的策略。
2.当策略很多的时候,类的数量就会很多。
具体案例
在对我自己的按理进行讲解的时候,我会不断的升级使用方式,来达到策略模式的最佳使用方式。
案例是我自己想的可能稍有些奇葩,但是最终目的是为了自己和看文章的同学能理解到位了,如有冒犯,请多担待
2019年年末,北京发现了外地来的鼠疫患者,我只是看到了消息,还好没什么状况。然后就和家人讨论:如果疫情真的来了,家里要做什么准备,然后每个人说自己的看法。
我哥:
1.我肯定要先买口罩
2.坐最快的火车直接回老家
3.回去以后有地里种的小麦和蔬菜养着肯定饿不死
我姐:
1.我也先买口罩
2.然后去最近的超市采购主食,要囤至少三个月的主食在家里
3.然后买一些能放置时间长的蔬菜,再买一些平时吃的东西,
我:
1.我打算什么都不做
根据上面的疫情来了后我们三个商量出的三种办法做三个策略类
首先定义一个接口
下面展示一些 内联代码片
。
public interface IStrategy {
void doSomething();
}
然后根据商量好的策略编写策略类
public class GoHomeStrategy implements IStrategy{
@Override
public void doSomething() {
System.out.println("买口罩");
System.out.println("坐车回家");
System.out.println("种地务农");
}
}
public class GoHomeStrategy implements IStrategy{
@Override
public void doSomething() {
System.out.println("买口罩");
System.out.println("坐车回家");
System.out.println("种地务农");
}
}
public class WaitDeathStrategy implements IStrategy{
@Override
public void doSomething() {
System.out.println("什么都不做");
}
}
使用方式
直接使用创建策略类对象
封装好的策略类,并不一定是为了客户端同学使用的,也可以是为了我们自己使用的。也不一定是用参数控制,策略类可以直接具象化到类对象的创建上。并不完全符合开闭原则,但是却也是最小的范围的修改,代价很小。
public class SwitchDemo {
public static void main(String[] args) {
IStrategy iStrategy = new WaitDeathStrategy();
iStrategy.doSomething();
}
}
执行结果如下
我们也可以换个策略,执行结果如下
可以将策略类在项目启动时初始化,将策略key与策略类绑定,使用时,直接使用策略key
public class SwitchDemo {
public static void main(String[] args) {
Map<String, IStrategy> map = new HashMap<>();
map.put("gohome", new GoHomeStrategy());
map.put("goshopping", new GoShoppingStrategy());
map.put("waitdeath", new WaitDeathStrategy());
IStrategy iStrategy = map.get("gohome");
iStrategy.doSomething();
}
}
执行结果如下
通常我们不想让策略的使用者与策略关联太紧密,因此初始化策略应该对使用者屏蔽,那我们就来创建一个策略上下文来持有所有的策略
public class StrategyContext {
private static Map<String, IStrategy> map = new HashMap<>();
static {
map.put("gohome", new GoHomeStrategy());
map.put("goshopping", new GoShoppingStrategy());
map.put("waitdeath", new WaitDeathStrategy());
}
public void doSomeThing(String key) {
map.get(key).doSomething();
}
}
public class SwitchDemo {
public static void main(String[] args) {
StrategyContext strategyContext = new StrategyContext();
strategyContext.doSomeThing("gohome");
}
}
执行结果如下
可以使用map+lamda函数,但是前提是策略需要做的事情不大复杂(我需要换个例子进行举例,并且我认为可能这种情况的应用并不多)
public class Calculator {
private static Map<String, BiFunction<Integer,Integer,Integer>> operatorMap = new HashMap<>();
static{
operatorMap.put("+",(a,b) -> a+b);
operatorMap.put("-",(a,b) -> a-b);
operatorMap.put("*",(a,b) -> a*b);
operatorMap.put("/",(a,b) -> a/b);
}
public int calculate(int left,String operator,int right){
return operatorMap.get(operator)!=null?operatorMap.get(operator).apply(left,right):-1;
}
}
如果你的策略实现不是单独的类例如如下这样
public class StrategyImpl{
public void doSomething1() {
System.out.println("买口罩");
System.out.println("坐车回家");
System.out.println("种地务农");
}
public void doSomething2() {
System.out.println("买口罩");
System.out.println("买主食");
System.out.println("买蔬菜");
}
public void doSomething3() {
System.out.println("什么都不做");
}
}
那么你的策略类可以改写成这样
public class StrategyContext1 {
private static Map<String, Function<String, String>> map = new HashMap<>();
private static StrategyImpl strategy;
static {
map.put("gohome",(arg1)->strategy.doSomething1());
map.put("goshopping", (arg1)->strategy.doSomething2());
map.put("waitdeath", (arg1)->strategy.doSomething3());
}
public void doSomeThing(String key) {
map.get(key).apply("");
}
}
我认为到底是否要使用这种关键是看你的策略是否要单独存放
//todo:持续增加中 开源项目中的策略模式讲解