没有难看的if-else的简单工厂实现

一、前言

最近接到一个任务,需要从NSQ消息队列获取业务数据,类型依靠message(16进制数)的前四位判断,判断完数据类型,再根据类型以不同的方式处理这些数据。目前推送过来的消息只有一部分类型是需要做处理的,后续还会增加其他类型的处理。

由于数据类型不单一而且要支持扩展,所以想要利用工厂设计模式,通过工厂返回可以处理该类型数据的处理类实例。可是工厂模式里面需要靠if-else做类型判断,十分丑陋,并且扩展新类型时带有侵入性,所以想了一种方法,让数据类型的枚举拥有能返回对应类型的处理器实例,这样枚举就同时能用拥有判断类型和返回对应类型处理器的功能,然后在工厂类中由这个枚举去动态地返回处理类实例,解决了问题。

二、实现

  1. 工厂:
@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;
    }
}
  1. 处理类抽象接口 以及 处理类
    处理器接口:
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
    }


}
  1. 枚举以及接口
    该接口返回一个数据处理类
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 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 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;
    }
}

你可能感兴趣的:(没有难看的if-else的简单工厂实现)