美文网首页java 设计Design Pattern 设计模式-收藏篇
枚举类+模板方法模式—动态调用接口

枚举类+模板方法模式—动态调用接口

作者: 小胖学编程 | 来源:发表于2020-01-13 19:10 被阅读0次

    实际项目中,中台系统会对外提供一个“通用”的接口,例如绑卡接口,而实际上,中台系统可能对接多家渠道,有些渠道需要进行个性化处理。

    但是即使对接不同渠道(银行),但处理逻辑本质上是相同的,那么我们就可以将公共的代码抽取出来,子类实现个性化的扩展。——而它本质上是模板方法模式

    当然,我们的想法是这样的:

    请求方式.png

    公共服务:大多数银行的处理逻辑;
    xx银行服务:个性化处理xx银行的逻辑;

    请求报文中可能存在商户号字段,而根据商户号,可以在缓存中获取该商户的银行标识。根据银行标识去Spring单例池中获取对应的Service,来完成业务处理。

    而枚举类,相当于一个数据字典,会保存某个接口下所有的类型、描述等信息。枚举类相当于工厂模式。根据枚举值的不同获取不同的service对象。

    public enum OpenAuthenServiceEnum {
    
        /**
         * 缺省情况下的Bean
         */
        DEFAULT("openAuthenService", OpenAuthenService.class, "通用绑卡业务接口"),
        /**
         * 子类特有Bean对象
         */
        MZ("XxopenAuthenService", XXOpenAuthenService.class, "XX业务-绑卡接口");
    
    
        /**
         * key:枚举值name,即业务标识。
         * value:枚举值对象。
         */
        private static Map<String, OpenAuthenServiceEnum> mapping = new HashMap<>(16);
        /**
         * key: 枚举值name,即业务标识
         * value:bean对象
         */
        private static Map<String, OpenAuthenService> serviceCache = new ConcurrentHashMap<>(16);
    
        static {
            for (OpenAuthenServiceEnum authenServiceEnum : values()) {
                mapping.put(authenServiceEnum .name(), authenServiceEnum);
            }
        }
    
        /**
         * Bean 名字,父类一般首字母小写,子类首字母大写。
         * 可以在{@code @service("aa")}手动指定BeanName
         */
        private String beanName;
        /**
         * Bean 类型: 用于在Spring容器中获取某类Bean
         */
        private Class<? extends OpenAuthenService> beanType;
        /**
         * 描述,用于打印日志
         */
        private String desc;
    
        OpenAuthenServiceEnum(String beanName, Class<? extends OpenAuthenService> beanType, String desc) {
            this.beanName = beanName;
            this.beanType = beanType;
            this.desc = desc;
        }
    
        public String getBeanName() {
            return beanName;
        }
    
        public Class<? extends OpenAuthenService> getBeanType() {
            return beanType;
        }
    
        public String getDesc() {
            return desc;
        }
    
        /**
         * 根据枚举对象名 获取泛枚举值对象(线程安全)
         *
         * @param enumName 枚举值的名字,不区分大小写
         * @return 若上送参数为null,则返回null,
         * 若上送的业务标识在mapping缓存,返回对应的枚举值对象。
         * 否则返回缺省 枚举值对象。
         */
        public static OpenAuthenServiceEnum resolve(String enumName) {
            return enumName != null ? mapping.getOrDefault(enumName.toUpperCase(), DEFAULT) : null;
        }
    
        /**
         * 泛型对象获取 Bean类(线程安全)
         * 下列代码等效于
         * OpenAuthenService service=serviceCache.get(this.getBeanName());
         * if(service==null){
         * synchronized(this){
         * service=serviceCache.get(this.getBeanName())
         * if(service==null){
         * service=SpringUtil.getBean(this.getBeanName(),this.getBeanType());
         * serviceCache.put(this.getBeanName(),service);
         * }
         * }
         * }
         *
         * @return Bean对象
         */
        public OpenAuthenService getBean() {
            return serviceCache.computeIfAbsent(this.name(),
                    K -> SpringUtil.getBean(this.getBeanName(), this.getBeanType()));
        }
    
    }
    

    使用方式:

    OpenAuthenServiceEnum bz = OpenAuthenServiceEnum.resolve("Bz");
    if(bz==null){
      throw new RuntimeException("业务参数不能为null");
    }
    //获取到对应的service,可以完成个性化的调用。
    OpenAuthenService bzBean = bz.getBean();
    

    这样的话,例如项目的可维护性和可扩展性,当然枚举类中还可以使用其他属性来完成个性化的配置。

    注:Spring中Bean名字一般是简写的类名,并且首字母小写,但是Bean的子类首字母要大写。

    相关文章

      网友评论

        本文标题:枚举类+模板方法模式—动态调用接口

        本文链接:https://www.haomeiwen.com/subject/kxdkactx.html