一、前言
最近接到一个任务,需要从NSQ消息队列获取业务数据,类型依靠message(16进制数)的前四位判断,判断完数据类型,再根据类型以不同的方式处理这些数据。目前推送过来的消息只有一部分类型是需要做处理的,后续还会增加其他类型的处理。
由于数据类型不单一而且要支持扩展,所以想要利用工厂设计模式,通过工厂返回可以处理该类型数据的处理类实例。可是工厂模式里面需要靠if-else做类型判断,十分丑陋,并且扩展新类型时带有侵入性,所以想了一种方法,让数据类型的枚举拥有能返回对应类型的处理器实例,这样枚举就同时能用拥有判断类型和返回对应类型处理器的功能,然后在工厂类中由这个枚举去动态地返回处理类实例,解决了问题。
二、实现
- 工厂:
@Component
public class MiYueDataProcessorFactory {
// 数据类型字段长度
private static final Integer PREX_LENGTH = 4;
public ProcessorBase getBy(byte[] msg) {
if (msg == null || msg.length <= 0) {
return null;
}
// 转换成16进制
String msgStr = StringTool.byteArray2HexString(msg);
if (StringUtils.isNotBlank(msgStr) && msgStr.length() >= PREX_LENGTH) {
String prex = msgStr.substring(0, PREX_LENGTH);
if (StringUtils.isNotBlank(prex)) {
// 获取枚举
MiYueDataTypeEnum miYueDataTypeEnum = MiYueDataTypeEnum.get(prex);
if (miYueDataTypeEnum != null) {
// 返回处理类
return miYueDataTypeEnum.returnProcessor();
}
}
}
return null;
}
}
- 处理类抽象接口 以及 处理类
处理器接口:
public interface ProcessorBase {
// 处理数据
void process(byte[] data);
}
某个处理器实现类:
@Component
public class AttendanceProcessor implements ProcessorBase {
public static AttendanceProcessor attendanceProcessor;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
// 注入静态的属性
attendanceProcessor = (AttendanceProcessor) applicationContext.getBean("attendanceProcessor");
}
@Override
public void process(byte[] data) {
// TODO
}
}
- 枚举以及接口
该接口返回一个数据处理类
public interface MiYueDataTypeProcessorReturner {
ProcessorBase returnProcessor();
}
枚举:
@Getter
@AllArgsConstructor
public enum MiYueDataTypeEnum implements MiYueDataTypeProcessorReturner {
ATTENDANCE("1234", "考勤") {
@Override
public ProcessorBase returnProcessor() {
return AttendanceProcessor.attendanceProcessor;
}
};
private static final Map<String, MiYueDataTypeEnum> ENUM_VALUE_MAP = Maps.newHashMap();
static {
for (MiYueDataTypeEnum typEnum : MiYueDataTypeEnum.values()) {
ENUM_VALUE_MAP.put(typEnum.getPrex(), typEnum);
}
}
private String prex;
private String reasonPhrase;
public static MiYueDataTypeEnum get(String prex) {
if (StringUtils.isBlank(prex)) {
return null;
}
return ENUM_VALUE_MAP.get(prex);
}
}
4.使用
@Component
@Slf4j
public class NsqMsgDeal implements NSQMessageCallback {
@Autowired
private MiYueDataProcessorFactory factory;
@Override
public void message(NSQMessage nsqMessage) {
try {
// 获取处理类
ProcessorBase processor = factory.getBy(nsqMessage.getMessage());
if (processor != null) {
// 处理数据
processor.process(nsqMessage.getMessage());
}
} catch (Exception e) {
// 数据处理异常处理。。。
} finally {
nsqMessage.finished();
}
}
}
新增新类型
只需增加枚举类型以及处理器实现。
-----------2021年3月26号补充----------------
另一种实现方式
枚举类
枚举类属性增加实现类的class对象
private Class clazz;
@AllArgsConstructor
@Getter
public enum CSApiCodeEnum {
APPRAISE("XXXXX", "XXX", XXX.class);
public static Map<String, CSApiCodeEnum> codeMap;
static {
codeMap = new HashMap<>();
CSApiCodeEnum[] values = CSApiCodeEnum.values();
for (CSApiCodeEnum codeEnum : values) {
codeMap.put(codeEnum.getCode(), codeEnum);
}
}
private String reasonPhrase;
private String code;
private Class clazz;
public static boolean valid(String code) {
if (StringUtils.isBlank(code)) {
return false;
}
return codeMap.containsKey(code);
}
public static CSApiCodeEnum getByCode(String code) {
return codeMap.get(code);
}
}
工厂类
根据code获取枚举后使用上下文的工具类获取实例
MyApplicationContextUtil.getApplicationContext().getBean(codeEnum.getClazz());
@Component
public class CSApiFactory {
public CSBaseApi getCSApi(String code) {
if (!CSApiCodeEnum.valid(code)) {
throw new xxException("XXXX");
}
CSApiCodeEnum codeEnum = CSApiCodeEnum.getByCode(code);
if (codeEnum != null) {
return (CSBaseApi) MyApplicationContextUtil.getApplicationContext().getBean(codeEnum.getClazz());
}
return null;
}
}
上下文工具类
@Component
public class MyApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
MyApplicationContextUtil.applicationContext = applicationContext;
}
}