需求:PDA线下扫描入库/出库货物上传txt文件到服务器,出入库中间的每一步流程都会上传特定后缀的txt文件,我需要解析它们并存到数据库中
解决方案:想到之前公司老大在写地推代码的时候用到了策略模式,正好类似于这次需求,开干
首先写基础接口类
public interface FileReadWay {
/**
* 保存txt内容到数据库
*
* @Author:jiangruliang@finscm.com
* @date: 2018/6/27/0027 13:56
*/
int saveTxt(File file);
}
然后根据每个文件类型去建立相应的实现类并重写saveTxt方法,这里就不把具体实现类贴出,然后还要写一个枚举类(在获取读取文件类型后,根据枚举获取对应参数):
这里的枚举类里面的type是每个txt的类型,clazz是每个具体实现类的路径,在后面反射用到
public enum PdaFileHandleEnum {
DJCY_FILE_READ("DJCY",
"com.itonghui.biz.pdafilehandle.service.impl.DJCYFileRead"), DJEB_FILE_READ("DJEB",
"com.itonghui.biz.pdafilehandle.service.impl.DJEBFileRead"), DJGB_FILE_READ("DJGB",
"com.itonghui.biz.pdafilehandle.service.impl.DJGBFileRead"), DJRY_FILE_READ("DJRY",
"com.itonghui.biz.pdafilehandle.service.impl.DJRYFileRead"), DJXH_FILE_READ("DJXH",
"com.itonghui.biz.pdafilehandle.service.impl.DJXHFileRead"), THCY_FILE_READ("THCY",
"com.itonghui.biz.pdafilehandle.service.impl.THCYFileRead"), THEB_FILE_READ("THEB",
"com.itonghui.biz.pdafilehandle.service.impl.THEBFileRead"), THGB_FILE_READ("THGB",
"com.itonghui.biz.pdafilehandle.service.impl.THGBFileRead"), THPG_FILE_READ("THPG",
"com.itonghui.biz.pdafilehandle.service.impl.THPGFileRead"), THRY_FILE_READ("THRY",
"com.itonghui.biz.pdafilehandle.service.impl.THRYFileRead"),;
private String type;
private String clazz;
private PdaFileHandleEnum(String type, String clazz) {
this.type = type;
this.clazz = clazz;
}
public String type() {
return type;
}
public String clazz() {
return clazz;
}
}
class FileReadContext {
private static Map<String,String> strategyMap = new HashMap<>();
//策略类:首先根据type去PdaFileHandleEnum找到对应类位置(clz),再通过反射获取其中saveText方法并执行
static void excuteFileRead(String type, File file) {
for (PdaFileHandleEnum t : PdaFileHandleEnum.values()){
strategyMap.put(t.type(), t.clazz());
}
String class_path=strategyMap.get(type);
try {
//PdaFileHandleEnum通过反射将PdaFileHandleEnum中映射的类实例化
Class clazz = Class.forName(class_path);
//调用saveTxt方法传参file
@SuppressWarnings("unchecked")
Method excute = clazz.getDeclaredMethod("saveTxt", File.class);
excute.invoke(clazz.newInstance(), file);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
这里先是将PdaFileHandleEnum中的type和clazz属性读到了一个strategyMap的HashMap中。
然后根据用户传入的type判断要实例化哪个类。
如果传入的是DJCY,则class_path为com.itonghui.biz.pdafilehandle.service.impl.DJCYFileRead。
通过Class.forName反射出该对象,并且执行其中的saveText方法。
这里的class_path就是PdaFileHandleEnum中的包路径了。
这样就实现替换switch{case}了。
因为每次调用excuteFileRead的时候都会重复将PdaFileHandleEnum中的type和clazz属性读到了一个strategyMap的HashMap中,这样是不合理的。
所以下面用了一个单例模式来解决这个问题。
新建一个FileReadSingleton
public class FileReadSingleton {
private static FileReadSingleton instance=null;
private FileReadSingleton(){
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new FileReadSingleton();
}
}
public static FileReadSingleton getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
private static Map<String,String> fileRead = new HashMap<>();
static{
for (PdaFileHandleEnum t : PdaFileHandleEnum.values())
fileRead.put(t.type(), t.clazz());
}
public String fileRead(String type){
return fileRead.get(type);
}
}
同时改造FileReadContext类
class FileReadContext {
//首先根据type去PdaFileHandleEnum找到对应类位置(clz),再通过反射获取其中saveText方法并执行
static void excuteFileRead(String type, File file) {
String clz = FileReadSingleton.getInstance().fileRead(type);
try {
//PdaFileHandleEnum通过反射将PdaFileHandleEnum中映射的类实例化
Class clazz = Class.forName(clz);
//调用saveTxt方法传参file
@SuppressWarnings("unchecked")
Method excute = clazz.getDeclaredMethod("saveTxt", File.class);
excute.invoke(clazz.newInstance(), file);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
e.printStackTrace();
}
}
}
完成。
感想:其实刚开始只是用策略模式实现了需求,后面由于switch{case}过多看的总是让人不爽于是找到了度娘,度娘告诉我反射能够解决,于是搜着找到了一篇博客,其实本篇博客思路几乎来自于该博客,感谢。