知道策略模式!但不会在项目里使用?

开发 前端
简单的来说当做某个事情有多个方式的时候,可以抽象为接口,然后每个实现是一种解决方式,由调用方来选择不同的实现方式。

前言

在学开发的第二年就开始听说要想代码写得好,一定要会设计模式。于是就兴致冲冲的啃了《Head First 设计模式》,看完之后对于策略模式映像很深刻,觉得这个模式好,易上手,应用广,我又能优化一波代码了,于是兴致冲冲的打开了我的 IDEA,开整!!!

策略模式初体验(错误示范)

在讲诉我的策略模式首秀前,我们先回顾下策略模式的基本概念。

策略模式

  • 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
  • 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

简单的来说当做某个事情有多个方式的时候,可以抽象为接口,然后每个实现是一种解决方式,由调用方来选择不同的实现方式。

理解了后我开始对我们的代码进行了重构,当时我第一家公司有这样一段代码,大概是这个意思(时间长了,我凭记忆重写的)。

有这样一个抽奖的方法,我们后台控制中奖率,不同的时候我们会调整不同的中奖策略。

public class NumStrategy {

enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的几率中奖
*/
RANDOM28;
}
/**
* 抽奖方法,根据不同的策略进行抽奖
* @param randomEnum
* @return true:代表中奖 false:代表没中奖
*/
public boolean luckDraw(RandomEnum randomEnum){
if(randomEnum.equals(RandomEnum.AVERAGE)){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}else if(randomEnum.equals(RandomEnum.RANDOM28)){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
return false;
}
}

我一看,这不就是妥妥的策略模式吗?开搞。

一顿改造之后变成了这样:

public class NumStrategy2 {
enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的几率中奖
*/
RANDOM28;
}
/**
* 抽奖方法,根据不同的策略进行抽奖
* @param randomEnum
* @return
public boolean luckDraw(RandomEnum randomEnum){
if(randomEnum.equals(RandomEnum.AVERAGE)){
return new AverageStrategy().luckDraw();
}else if(randomEnum.equals(RandomEnum.RANDOM28)){
return new Random28Strategy().luckDraw();
}
return false;
}
interface LuckDrawStrategy{
boolean luckDraw();
}
class AverageStrategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}
}
class Random28Strategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
}

}

改造完成之后我满意的提交了代码,但是在组长 review 的时候给我又改了回来。说你整这么多类干嘛?我理直气壮的说我这是用策略模式优化代码。他说没必要,先改回去吧。

我愤愤的接受了,但心里想着:哎,你连策略模式都不懂?

经过这么多年,我开始理解我当时的做法其实不对,本来很简单的代码,而且里面的逻辑不会有变动,其实不需要抽象出来。我的改动有过度设计之嫌。把原来的30行代码搞成了80行

一报还一报,这几年我见过太多次当年的我这样写代码的了。

即为了用设计模式而用设计模式。而忘了设计模式的初衷是为了代码更易理解,更可靠,更易维护。

甚至还见过有人学了策略模式后说要把项目里所有的 if/else都安排上策略模式。

梅开二度

又过了一年多,在一次面试的时候,也有着关于策略模式的讨论。

【面试官】问:你说你用过策略模式,请问你为什么用它?

【我】:为了抽离各个不同实现逻辑,优化 if/else,使代码更简单易懂

【面试官】:你具体说说,怎么去掉的 if/else

【我】:内心 OS(背的知识点,我也好久没用了啊)。我硬着头皮说,我可以使用工厂模式+策略模式来做。

【面试官】:那你工厂模式的那里不是也要用 if/else 判断吗?

【我】:。。。额。唔。。。那确实还是要用到 if/else

把我问住了,我支支吾吾的回答确实还是要 if/else 来判断一次,只不过把判断移到了工厂模式里面去了。

我下来后又去实践了下,想着放在 map 里行不行呢?

public class NumStrategy3 {
enum RandomEnum{
/**
* 平均策略
*/
AVERAGE,
/**
* 80%的几率中奖
*/
RANDOM28;
}
static Map<RandomEnum,LuckDrawStrategy> map = new HashMap<>();
static{
map.put(RandomEnum.RANDOM28,new Random28Strategy());
map.put(RandomEnum.AVERAGE,new AverageStrategy());
}
/**
* 抽奖方法,根据不同的策略进行抽奖
* @param randomEnum
* @return
public boolean luckDraw(RandomEnum randomEnum){
LuckDrawStrategy luckDrawStrategy = map.get(randomEnum);
return luckDrawStrategy.luckDraw();
}
interface LuckDrawStrategy{
boolean luckDraw();
}
static class AverageStrategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 50;
}
}
static class Random28Strategy implements LuckDrawStrategy{
@Override
public boolean luckDraw(){
Random random = new Random();
int num = random.nextInt(100);
return num >= 20;
}
}
}

终于是解决了 if/else 的情况,不过这样很短的 if/else,里面逻辑不怎么变动时,我个人是不建议用策略模式,这里只是示例。

推荐用法

又过了几年,当初的菜鸟也成长为了一个老鸟。

当时项目里有这样一个代码:

下面的代码我进行了一些简化,我们有一个功能,对页面上的指标进行计算,不同的指标对应不同的计算方法。页面上指标一期做 4 个,后续会做到十几个。

public interface TransferService {
String transfer();
}
@Service
public class SearchTransformService {
@Autowired
private UserTransferService userTransferService;
@Autowired
private AgeTransferService ageTransferService;
@Autowired
private InterestTransferService interestTransferService;
/**
* 根据不同的编码进行转换
* @param code
* @return
public String transform(String code){
if(code.equals("user")){
return userTransferService.transfer();
}else if(code.equals("age")){
return ageTransferService.transfer();
}else if(code.equals("interest")){
return interestTransferService.transfer();
}
return "";
}
}

可以看到这样的业务场景下,这样的写法 if/else 就会很长,后续十几个的情况下就很难维护。另外 code 使用的是魔数,也是不好的一种写法。我对此进行了优化如下:

先将 code 用枚举定义

   enum CodeEnum {
USER("user"),
AGE("age"),
INTEREST("interest"),
;
private String code;

public String getCode() {
return code;
}
CodeEnum(String code) {
this.code = code;
}
private static final Map<String, CodeEnum> map = Arrays.stream(CodeEnum.values()).collect(Collectors.toMap(CodeEnum::getCode, Function.identity()));
public CodeEnum of(String code) {
return map.get(code);
}
}

原有的接口上增加一个transCode方法,每个实现需要声明是对应哪个编码的实现

public interface TransferService {

String transfer();

CodeEnum transCode();
}

@Service
public class AgeTransferService implements TransferService {
@Override
public String transfer(){
return null;
}

@Override
public CodeEnum transCode(){
return CodeEnum.AGE;
}
}

使用 map 存储编码对应的实现类的关联关系,以此来获取对应的转换器实现类

@Service
public class SearchTransformService implements InitializingBean {
@Autowired
private List<TransferService> transferServiceList;
private Map<CodeEnum, TransferService> transferServiceMap;
@Override
// 项目启动时将实现类放入到map中去
public void afterPropertiesSet() throws Exception {
transferServiceMap = transferServiceList.stream().collect(Collectors.toMap(TransferService::transCode, Function.identity()));
}
/**
* 根据不同的编码进行转换
* @param code
* @return
public String transform(String code){
TransferService transferService = transferServiceMap.get(CodeEnum.of(code));
Assert.notNull(transferService,"找不到对应的转换器");
return transferService.transfer();
}
}

重构后是不是就很简洁了呢?如果后续新增新的编码转换器,只需要先在枚举里定义,然后新增实现类实现方法就行了,不需要关心是怎么调用的,只关心具体的实现逻辑,降低了维护成本。

这才是策略模式的真正应用吧。不要再乱用了,哈哈哈。

责任编辑:姜华 来源: 今日头条
相关推荐

2018-07-16 09:36:05

2022-06-23 11:22:12

LinuxLinusLinux 社区

2012-06-20 14:38:32

2010-08-26 10:06:18

微软Hyper-VVMworld

2018-04-27 15:00:54

2009-03-13 19:04:57

Windows 7发布日期

2010-10-19 10:44:49

李开复

2022-05-05 19:00:22

PythonPython特性

2019-10-15 16:02:23

戴尔

2020-06-28 14:18:51

Android 11功能谷歌

2020-07-20 13:59:17

服务器项目工程打包

2013-12-27 15:42:34

小米闪购

2015-07-17 11:35:22

Windows 10功能

2011-11-03 16:09:36

2021-10-10 13:44:09

微信支付iPhone手机移动应用

2020-05-13 08:45:19

数据中心IT云服务

2020-05-18 09:43:53

数据中心云服务云计算

2020-06-28 14:51:15

容器Gartner软件提供商

2022-07-05 08:05:00

策略模式接口实现类

2013-09-25 13:00:31

Linux开源Linux社区
点赞
收藏

51CTO技术栈公众号