没有难看的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<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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容