前言
你是否在开发中会遇到这种情况,某段逻辑一开始只有两个分支,你很快的使用if else搞定这段逻辑,小半天搞定之后开始了快乐摸鱼。
这时候,你最爱的产品经理一次又一次的出现,打破了这份宁静,告诉你这段逻辑又新增了一个客户,你又得啪嗒啪嗒堆叠if else,但是你还是搞得定,仍然啪嗒啪嗒敲着键盘搞定,你还是可以勉强美滋滋的。
某一天,你的开发组长review你的代码后,对你一阵爱的教育:"这鬼代码又臭又长,后面别人接手代码你愿意日常对他开设代码爱的补习班吗?"
策略模式,美化可维护代码的求生技能
什么是策略模式
策略模式即将不同的逻辑封装到不同的类中,然后定义一个策略管理者来管理这组策略,后续调用者需要某个策略时,只需告诉策略管理者需要拿个策略即可得到所需策略,当业务需要新的策略时,只需根据策略的规范添加一个新的策略类即可,这样做就保证了开闭原则(你加逻辑不能堆代码到当前类中,必须基于扩展实现)
为演示举个栗子,给你提个鬼需求
现在你的产品经理给你提了一个需求,你要做一个文件解析系统,客户使用你的系统时会传递你一个fileType的参数
0 A类型文件
1 B类型文件
其他参数 默认类型文件
复制代码
然后你根据这个参数,去完成他们的文件解析。
然后你的狗产品补充了一句:"后续可能还会增加更多的文件解析需求哦"
你的组长给你补刀:"写好看点"
你回组长:"啥叫好看,我代码字体改为黑体?"
组长沉默,看了身边的水果刀。
你懂了。
定义策略接口
既然我们要制作一个文件解析模块,且需要基于策略模式完成,那么我们就得遵循开闭原则,所以我们需要定义一个接口,规范所有文件解析策略的规范。
package com.example.Strategy.config;
public interface IFileStrategy {
/**
* 获取当前策略能够解析的文件类型
* @return
*/
FileTypeResolveEnum gainFileType();
/**
* 解析当前文件的具体实现逻辑
* @param objectparam
*/
void resolve(Object objectparam);
}
复制代码
使用枚举规范文件类型定义
你的狗产品说了后续可能会出现更多的文件解析类型,那么为了避免其他开发接手你的代码时,使用字符串而出现手抖搞错逻辑的情况,我们就需要定义一个枚举来保证文件类型定义的准确性。
package com.example.Strategy.config;
/**
* 文件类型枚举
*/
public enum FileTypeResolveEnum {
// a类型文件
File_A_RESOLVE,
// b类型文件
File_B_RESOLVE,
// 默认类型文件
File_DEFAULT_RESOLVE
}
复制代码
实现各种文件对应的策略
接下来我们就基于上述的接口完成对应的文件解析策略
package com.example.Strategy.support;
import com.alibaba.fastjson.JSON;
import com.example.Strategy.config.FileTypeResolveEnum;
import com.example.Strategy.config.IFileStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* A文件解析策略
*/
@Component
public class AFileResolve implements IFileStrategy {
protected static final Logger logger = LoggerFactory.getLogger(AFileResolve.class);
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("解析A类型的文件中,请求参数 {}", JSON.toJSONString(objectparam));
}
}
复制代码
package com.example.Strategy.support;
import com.alibaba.fastjson.JSON;
import com.example.Strategy.config.FileTypeResolveEnum;
import com.example.Strategy.config.IFileStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* B文件解析策略
*/
@Component
public class BFileResolve implements IFileStrategy {
protected static final Logger logger = LoggerFactory.getLogger(BFileResolve.class);
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_B_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("解析B类型文件中,请求文件中 {}", JSON.toJSONString(objectparam));
}
}
复制代码
package com.example.Strategy.support;
import com.alibaba.fastjson.JSON;
import com.example.Strategy.config.FileTypeResolveEnum;
import com.example.Strategy.config.IFileStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 默认文件解析策略
*/
@Component
public class DefaultFileResolve implements IFileStrategy {
protected static final Logger logger = LoggerFactory.getLogger(DefaultFileResolve.class);
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_DEFAULT_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("解析默认文件中,请求参数 {}", JSON.toJSONString(objectparam));
}
}
复制代码
对外暴露这组策略的服务api
接下来就是实现策略模式的核心,笔者基于spring的api将上述策略的存到一个map的容器中,后续调用者使用传对应的fileType参数值,笔者就去map中查找是否有对应的策略,若有则调用这个策略的解析方法完成文件解析,代码如下所示:
package com.example.Strategy.service;
import com.example.Strategy.config.FileTypeResolveEnum;
import com.example.Strategy.config.IFileStrategy;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class FileResloveService implements ApplicationContextAware {
private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> fileStrategyMap = applicationContext.getBeansOfType(IFileStrategy.class);
fileStrategyMap.values().forEach(fileResloveObj -> iFileStrategyMap.put(fileResloveObj.gainFileType(), fileResloveObj));
}
public void resolve(FileTypeResolveEnum filetype, Object objectparam) {
if (iFileStrategyMap.containsKey(filetype)) {
iFileStrategyMap.get(filetype).resolve(objectparam);
return;
}
iFileStrategyMap.get(FileTypeResolveEnum.File_DEFAULT_RESOLVE).resolve(objectparam);
}
}
复制代码
可能很多读者会对setApplicationContext有点陌生,这个就是spring为我们提供的一个强大杀气,我们的类只需继承ApplicationContextAware,spring会将bean初始化的时候判断这个bean是否是ApplicationContextAware类,如果是则会将spring将会通过你继承ApplicationContextAware所得来的方法setApplicationContext以参数形式将applicationContext给你进行蹂躏。
具体可以参照笔者的这篇文章
# 手写spring第八章-定义标记类型Aware接口,实现感知容器对象
测试用例
package com.example.Strategy.service;
import com.alibaba.fastjson.JSONObject;
import com.example.DesignPattern.DesignPatternApplication;
import com.example.Strategy.config.FileTypeResolveEnum;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DesignPatternApplication.class)
class FileResloveServiceTest {
protected static final Logger logger = LoggerFactory.getLogger(FileResloveServiceTest.class);
@Resource
private FileResloveService fileResloveService;
@Test
void resolve() {
JSONObject param=new JSONObject();
param.put("啥文件","这就是A类型文件");
fileResloveService.resolve(FileTypeResolveEnum.File_A_RESOLVE,param);
}
}
复制代码




近期评论