这里涉及到一个关键词:策略模式,那么到底什么是策略模式呢?本文就来好好给大家讲讲策略模式,大家可以带着如下几个问题来阅读本文:
 
1. 如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考)
2. 使用策略模式优化if-else,会不会带来什么副作用呢?
3. 实现策略模式是否有更好的方式呢?
 
1. 策略模式如何优化代码解构
 
要会带这个问题,需要先弄清楚策略模式的定义,首先来看策略模式的教科书定义:策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。 策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。 策略模式是一种对象行为型模式。
 
这里的算法其实就是业务逻辑,为了更形象,干脆将其理解为一个函数。其实这个定义的基本意思就是说,根据策略选择要执行的函数,而每一个策略都会有一个标识,可以称为key。而当前策略要执行的函数,可以称为value,其实就是使用key寻找value,然后执行vlaue的过程。也就是说,一个key对应一个value,从这层意思上理解,就是if-else要干的事。
 
 那么策略模式到底优化了什么呢?
 
其实策略模式的核心思想与 if else如出一辙,根据不同的key动态的找到不同的业务逻辑(要执行的函数)。那它就只是如此吗?
 
实际上,我们口中的策略模式其实就是在代码结构上调整,用接口+实现类+分派逻辑来使代码结构可维护性更好。
 
 一般教科书上讲到接口与实现类就结束了,其他博客上会带上提及分派逻辑。这里就不啰嗦了。现在总结一下:即使用了策略模式,所有的业务逻辑一样都少不了,改写的仍然需要写。到逻辑分派的时候,还是变相的if-else。而策略模式的优化点是抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 使用if-else 更好维护。
 
2. 使用策略模式优化if-else,会不会带来什么副作用呢?
 
我估计肯定会有很多同学这么想:我的业务逻辑就几行,你给我整一大堆类定义?有必要这么麻烦吗?我看具体的业务逻辑还需要去不同的类中,简单点不香吗!
 
其实这里的不满也正是策略模式的缺点:
 
(1) 策略类会增多
(2) 业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑
 
针对传统策略模式的缺点,在这分享一个实现思路,这个思路已经帮我们团队解决了多个复杂if else的业务场景,理解上比较容易,在技术上要使用到Java8的特性:Map与函数式接口。
 
废话少说,直接看代码:
 
先看两个方法:
1. processResult() :使用if-else的方法
2. processPolicyResult():使用策略模式的方法。在该方法中事先在Map中定义好了“判断条件”与“业务逻辑”的映射关系,具体看代码吧!
 
/**
* 策略模式演示类
*/
public class MyService { /**
* 使用if-else的解决方案
*/
public String processResult(String key) {
if ("checkvalue1".equals(key)) {
return "business logic1";
} else if ("checkvalue2".equals(key)) {
return "business logic2";
}else if ("checkvalue3".equals(key)) {
return "business logic3";
}else if ("checkvalue4".equals(key)) {
return "business logic4";
}else if ("checkvalue5".equals(key)) {
return "business logic5";
}else if ("checkvalue6".equals(key)) {
return "business logic6";
}else if ("checkvalue7".equals(key)) {
return "business logic7";
}else if ("checkvalue8".equals(key)) {
return "business logic8";
}else if ("checkvalue9".equals(key)) {
return "business logic9";
}
return "error;
} /**
* 用于业务逻辑分派Map
* Function为函数式接口,下面代码中 Function<String, String> 的含义是接收一个String类型的变量,返回一个String类型的结果
*/
private Map<String, Function<String, String>> myDispatcher = new HashMap<>(); /**
* 使用策略模式的方法
*/ public void policyInit() {
myDispatcher.put("checkvalue1", key -> String.format("business logic1 for %s1", key));
myDispatcher.put("checkvalue2", key -> String.format("business logic2 for %s2", key));
myDispatcher.put("checkvalue3", key -> String.format("business logic3 for %s3", key));
myDispatcher.put("checkvalue4", key -> String.format("business logic4 for %s4", key));
myDispatcher.put("checkvalue5", key -> String.format("business logic5 for %s5", key));
myDispatcher.put("checkvalue6", key -> String.format("business logic6 for %s6", key));
myDispatcher.put("checkvalue7", key -> String.format("business logic7 for %s7", key));
myDispatcher.put("checkvalue8", key -> String.format("business logic8 for %s8", key));
myDispatcher.put("checkvalue9", key -> String.format("business logic9 for %s9", key));
} public String processPolicyResult(String key) {
//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一个lambda表达式
Function<String, String> result = myDispatcher.get(key);
if (result != null) {
//执行这段表达式获得String类型的结果
return result.apply(key);
}
return "error";
}
}

下面是调用代码:

public class RunPolicy {
private MyService myService; public String test(String key) {
return myService.processPolicyResult(order);
}
}

从这段代码中可以看到很多好处,例如:

(1)在policyInit()方法中直观地看到“判断条件”与“业务逻辑”的映射关系;
(2)不需要单独定义接口与实现类,直接使用现有的函数式接口即可;
 
3. 策略模式在真实场景中的应用
 
可能有的同学会说,我的条件判断可能非常复杂,而前面的案例只有一个条件判断(key),其实这就和数据库中通过单个字段作为索引,还是使用复合索引(多个字段共同组成索引)的问题。我们也可以用复合条件来实现策略模式,上代码:
/**
* 策略模式类
*/
public class PolicyService { private Map<String, Function<String, String>> myDispatcherMulti = new HashMap<>(); /**
* 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式
*/
@PostConstruct
public void dispatcherMuitInit() {
myDispatcherMulti.put("key_order1", key -> String.format("business logic1 for %s", key));
myDispatcherMulti.put("key_order2_order3", key -> String.format("business logic2 for %s", key));
myDispatcherMulti.put("key_order1_order2_order3", key -> String.format("business logic3 for %s", key));
} public String processMuti(String key, int level) {
//根据level获取不同的key
String dKey = getDispatcherKey(key, level); Function<String, String> result = myDispatcherMuti.get(dKey);
if (result != null) {
//执行这段表达式获得String类型的结果
return result.apply(key);
}
return "error";
} /**
* 根据level生成不同层次的key
*/
private String getDispatcherKey(String key, int level) {
StringBuilder k = new StringBuilder("key");
for (int i = 1; i <= level; i++) {
k.append("_" + order + i);
}
return k.toString();
}
} /**
* 测试代码
*/
public class TestPolicyMulti { private PolicyService policyService; public String test(String key, int level) {
return policyService.processMuti(key, level);
}
}
 
在这段代码中,key是满足一定规则的复合条件,只要设计好key的生成规则就一切ok!
 
可能还会有很多同学问,我的业务逻辑有很多行,在dispatcherMuitInit()方法的Map中直接写不会很长吗?直接写当然长了,我们可以抽象出一个服务类专门放业务逻辑,然后在定义中调用它就可以了,代码如下:
 
/**
* 专门放业务逻辑的服务类
*/
public class ServiceUnit { public String task1(String key) {
return "业务逻辑1";
}
public String task2(String key) {
return "业务逻辑2";
}
public String task3(String key) {
return "业务逻辑3";
}
public String task4(String key) {
return "业务逻辑4";
}
}
/**
* 使用策略模式的类
*/
public class PolicyService {
private ServiceUnit serviceUnit; private Map<String, Function<String, String>> myDispatcher = new HashMap<>(); /**
* 初始化规则映射
*/
public void dispatcherInit() {
myDispatcher.put("key_order1", key -> serviceUnit.task1(key));
myDispatcher.put("key_order1_order2", key -> serviceUnit.task2(key)));
myDispatcher.put("key_order1_order2_order3", key -> serviceUnit.task3(key));
myDispatcher.put("key_order1_order2_order3_order4", key -> serviceUnit.task4(key));
} public String process(String key, int level) {
// 根据level生成对应的key
String dKey = getDispatcherKey(key, level); Function<String, String> result = myDispatcher.get(dKey);
if (result != null) {
//执行这段表达式获得String类型的结果
return result.apply(order);
}
return "error";
} /**
* 根据level生成对应的key
*/
private String getDispatcherKey(String key, int level) {
StringBuilder k = new StringBuilder("key");
for (int i = 1; i <= level; i++) {
k.append("_" + order + i);
}
return k.toString();
}
}

总结:

 
1. 如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考)
抽象了出了接口,将业务逻辑封装成的实现类,任意地替换。在复杂场景(业务逻辑较多)时比直接 使用if-else 更好维护。
 
2. 使用策略模式优化if-else,会不会带来什么副作用呢?
 
其实使用策略模式大多数时候会带来很多好处,不过也会有一些不足的:
(1)策略类比较多;
(2)业务逻辑分散到各个实现类中,而且没有一个地方可以俯览整个业务逻辑;
3. 实现策略模式是否有更好的方式呢?
 
可以用函数函数式接口实现业务逻辑,这样可以更直观观察策略和执行逻辑的关系。
 
喜欢我的文章,请扫描下面二维码关注 『极客起源』 公众号
 

也可以扫描下面二维码加李宁老师的微信:unitymarvel

 
 
 

小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了的更多相关文章

  1. 链家鸟哥:从留级打架问题学生到PHP大神,他的人生驱动力竟然是?

    链家鸟哥:从留级打架问题学生到PHP大神,他的人生驱动力竟然是?| 二叉树短视频 http://mp.weixin.qq.com/s/D4l_zOpKDakptCM__4hLrQ 从问题劝退学生到高考 ...

  2. VS2012+SQL2008+ODBC编程,第一篇博客,写的不好忘各位大神指点一二~

    近期写一个数据库的课程设计,用的是C++ MFC .最開始用的是ADO技术,可是苦于网上大部分的教程都是VC6.0的,对着教程敲了4,5遍还是执行不成功.我用的IDE是VS2012,毕竟VC6.0和V ...

  3. Perm排列计数(新博客试水,写的不好,各路大神见谅)

    B. Perm 排列计数 内存限制:512 MiB 时间限制:1000 ms 标准输入输出   题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i&l ...

  4. linux 删除命令注意事项(大神勿看)

    在做软连接的时候,想到一个问题,如果删除软连接,会不会删除源文件. 如果删除是真删除的话,一旦操作错误那就后悔去吧. 效果是这样的:#rm -rf pp/          如果pp是软连文件夹,那么 ...

  5. 求大神帮解答calendar日期插件的问题

    小颖最近公司的项目里用了一款日期插件  calendar.js  但是在用的过程中遇到了难题,就是当日期只需要选择具体的月份就可以了,不需要再选具体日期时,小颖解决不了,只能让它默认显示出月份,但是月 ...

  6. JS高级群的日常!写一个从10到0的倒计时,用console.log打印,不可以用 setInterval!本来说好的研究avalonJS最后演变成了看着大神在那边互相比拼实力。。

      JS高级群的日常!写一个从10到0的倒计时,用console.log打印,不可以用 setInterval!本来说好的研究avalonJS最后演变成了看着大神在那边互相比拼实力..   小森执行一 ...

  7. 厉害了,Google大神每天写多少行代码?

    文章转自开源中国社区,编译自:Quora Quora上有个有趣的问题:Google工程师们每天写多少行代码? Google 的 AdMob 全栈工程师 Raymond Farias 在 Quora 发 ...

  8. 【同行说技术】iOS程序员从小白到大神必读资料汇总

    在文章<iOS程序员从小白到大神必读资料汇总(一)>里面介绍了很多iOS入门学习的资料,今天小编就发几篇技术进阶的文章,快来看看吧! 一.iOS后台模式开发指南 这个教程会教你在什么时候怎 ...

  9. 真想用c#开发个 wp五笔输入法。。。奈何网上资料太少,源码都是c++写的。求大神指点!!!

    真想用c#开发个 wp五笔输入法...奈何网上资料太少,源码都是c++写的.求大神指点!!!!

随机推荐

  1. Java实现 LeetCode 401 二进制手表

    401. 二进制手表 二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59). 每个 LED 代表一个 0 或 1,最低位在右侧. 例如,上面的二进制手表 ...

  2. Java实现 LeetCode 78 子集

    78. 子集 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] 输出: [ [3], [1], ...

  3. java实现字符串比较

    标题:字符串比较 我们需要一个新的字符串比较函数compare(s1, s2). 对这个函数要求是: 1. 它返回一个整数,表示比较的结果. 2. 结果为正值,则前一个串大,为负值,后一个串大,否则, ...

  4. Dockerfile 解析

    Dockerfile Dockerfile是用来构建Docker镜像的构建文件,是由一系列参数和命令构成的脚本. 构建的三个步骤:1.编写Dockerfile文件  2.docker build  3 ...

  5. 简谈Java语言的继承

    Java语言的继承 这里简谈Java语言的三大特性之二——继承. Java语言的三大特性是循序渐进的.是有顺序性的,应该按照封装-->继承-->多态这样的顺序依次学习 继承的定义 百度百科 ...

  6. 掌握SpringBoot-2.3的容器探针:深入篇

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:原创分类汇总及配套源码,涉及Java.Docker.K8S.DevOPS等 关于<Spr ...

  7. Java 多线程基础(三) start() 和 run()

    Java 多线程基础(三) start() 和 run() 通过之前的学习可以看到,创建多线程过程中,最常用的便是 Thread 类中的 start() 方法和线程类的 run() 方法.两个方法都包 ...

  8. redis的5种数据结构和基本操作

    1.字符串(string) 1.1设置值 set key value [ex seconds] [px milliseconds] [nx|xx] 例如: 127.0.0.1:6379> set ...

  9. Python的数据的基本类型

    基本数据类型 int整数 str 字符串   一般不存放大量的数据 bool  布尔值,用来判断. True,False    list  列表.存放大量数据,[]表示,里面可以放各种数据类型     ...

  10. fopen函数中的mode参数

    fopen FILE * fopen ( const char * filename, const char * mode ); 其中,参数mode可取以下值: "r"read: ...