还在整天写if else ?

栏目: Java · 发布时间: 8年前

内容简介:还在整天写if else ?

在业务团队写代码,经常会遇到这种情况,产品经理来个需求,好的,我们加个字段,加个if else. 谢晞鸣最近总结了一些比 if else 优雅一点的套路,能够让自己代码的逼格看起来稍微高点。很多时候分支是不可避免的,不同的业务,肯定要走不同的分支,我这里所谓的优雅套路,本质上就是用一个 Map 将 “条件””业务处理逻辑“ 两者映射起来。

这里假设我们的项目是基于Spring的。文章中列的代码是不完整的,说明意思即可。

if else

有这样一种业务场景,我们做保险产品项目,不同产品核保的逻辑是不一样的(对年龄、限额、职业、性别等等的要求都不一样),假设有两个字段, productCategory, productSubCategory 用于区分不同类型的保险(万能险、投连险、养老险、产险等)。我们可以这么写

if (productCategory.equals("XXX")){
    processXXX();
} else if(productCategory.equals("YYY")){
    processYYY();
}

优雅的套路

我们也可以设计这样一个结构来处理这种业务,将我们的 ”条件”(即产品类型)“业务处理逻辑”(即核保逻辑) 映射起来, 下面是一个很简单的 demo:, 源码地址

还在整天写if else ?

先来介绍各个package:

  • resource, 项目入口层,可以理解成我们常见的controller、facade 之类的层
  • service, 业务逻辑层
  • registry, 我们的注册中心,负责业务逻辑的路由
  • dto, 接口调用传递的数据对象
  • common, 一些公共组件

后面的介绍中删减了get/set方法,先看service层

public interface FdxProcessor{
    void process(FdxDto<?> fdxDto);
}
public class FdxDto<T>{
    private String productCategory;
    private String productSubCategory;
    private T param;
}

面向接口编程,每种类型的产品的处理逻辑都必须实现该接口,这里的 DTO 是一个泛型,为了支持各种类型的数据,也定义了一些必传的参数,比如这里的两个category.接下来是一个关键的抽象类,这里完成了业务逻辑的注册。

public abstract class AbstractFdxProcessorimplements FdxProcessor,InitializingBean,BeanNameAware{
    @Autowired
    private FdxProcessorRegistry fdxProcessorRegistry;
    protected String beanName;

    public void setBeanName(String name){
        beanName = name;
    }

    protected abstract FdxProcessorRegistry.FdxKeyPairgetKeyPair();

    @Override
    public void afterPropertiesSet()throwsException{
        fdxProcessorRegistry.put(getKeyPair(), beanName);
    }
}
  1. 实现业务接口FdxProcessor
  2. 实现 BeanNameAware 接口,为了获得每个processor 在 Spring context 中的 beanName
  3. 实现 InitializingBean 接口(这是 Spring 提供的一些生命周期接口中的一个),在 Spring 完成该Bean初始化之后,将 beanName 注册到注册中心去
  4. 注意抽象方法 getKeyPair, 这是子类必须实现的,用于构造注册时用的key,即之前说的 “条件” 。关于这个 “条件” ,有多种实现方式,后面再细聊。

然后就是每种产品具体的业务逻辑实现:

/**
 * XXX 产品的业务逻辑
 */
@Service
public class XxxFdxProcessorImplextends AbstractFdxProcessor{
    @Override
    public void process(FdxDto<?> fdxDto){
        //do business
    }

    @Override
    protected FdxProcessorRegistry.FdxKeyPairgetKeyPair(){
        return new FdxProcessorRegistry.FdxKeyPair("XXX","XXX");
    }
}

/**
 * YYY 产品的业务逻辑
 */
@Service
public class YyyFdxProcessorImplextends AbstractFdxProcessor{
    @Override
    public void process(FdxDto<?> fdxDto){
        //do business
    }

    @Override
    protected FdxProcessorRegistry.FdxKeyPairgetKeyPair(){
        return new FdxProcessorRegistry.FdxKeyPair("YYY", "YYY");
    }
}

接下来看下这个简单的注册中心,主要实现了

  • 注册,将 key 和 beanName 映射起来
  • 获取,通过 key 获得 beanName,然后通过 beanName 从 Spring Context 中获得真正的 bean
    @Component
    public class FdxProcessorRegistry{
        private Map<FdxKeyPair, String> config = new HashMap<FdxKeyPair, String>();
    
        public void put(FdxKeyPair key, String beanName){
            config.put(key, beanName);
        }
    
        publicFdxProcessorget(FdxKeyPair key){
            return SpringContextHolder.getBean(config.get(key));
        }
    
        public static class FdxKeyPair{
            private String productCategory;
            private String productSubCategory;
    
            public FdxKeyPair(String productCategory, String productSubCategory){
                this.productCategory = productCategory;
                this.productSubCategory = productSubCategory;
            }
    
            @Override
            public int hashCode(){
                //TODO
                return 1;
            }
    
            @Override
            public boolean equals(Object obj){
                //TODO
                return true;
            }
        }
    }
    

入口层这样使用的就可以了.

public void doFdx(FdxDto fdxDto){
    fdxProcessorRegistry.get(new FdxKeyPair(fdxDto.getProductCategory(), fdxDto.getProductSubCategory())).process(fdxDto);
}

套路的变种

最后,关于 “条件”“业务逻辑” 之间的映射,可能会有这样一些其他的实现方式,

1. 用于路由的条件比较简单

比如说只有一个category, 我们可以定义一个 annotation,比如

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public  @interface FdxCategory {
    StringproductCategory()default {};
}

将它用在XxxFdxProcessorImpl这些子类上,然后稍微修改一下AbstractFdxProcessor

public abstract class AbstractFdxProcessorimplements FdxProcessor,BeanPostProcessor{
    @Autowired
    private FdxProcessorRegistry fdxProcessorRegistry;

    publicObjectpostProcessAfterInitialization(Object bean, String beanName)throwsBeansException{
            FdxCategory fdxCategory = bean.getClass().getAnnotation(FdxCategory.class);
            //通过 fdxCategory 拿到 category, 作为 key 和 beanName 一起进入注册中心完成注册
        }
        return bean;
    }
}

2. 用于路由的条件比较复杂

可以自定义 Key 类,但是要注意重写好 equals 和 hashCode

3. 每套逻辑可能适用于多个category

这种情况可以将一些公共的逻辑抽象出来放在抽象类中,也可以在 “条件” 上做些文章,比如可以这么搞:

@Service
public class XyzFdxProcessorImplextends AbstractFdxProcessor{
    @Override
    protectedList<FdxKeyPair>getKeyPair(){
      List<FdxKeyPair> list = new LinkedList<FdxKeyPair>();
      list.add (new FdxKeyPair("XXX", "XXX"));
      list.add (new FdxKeyPair("YYY", "YYY"));
      list.add (new FdxKeyPair("ZZZ", "ZZZ"));
      return list;
    }
}

public abstract class AbstractFdxProcessorimplements FdxProcessor,InitializingBean,BeanNameAware{
    @Autowired
    private FdxProcessorRegistry fdxProcessorRegistry;
    protected String beanName;
    public void setBeanName(String name){
        beanName = name;
    }

    protected abstractList<FdxKeyPair>getKeyPair();

    @Override
    public void afterPropertiesSet()throwsException{
      List<FdxKeyPair> fdxKeyPairs =  getKeyPair();
      for(FdxKeyPair fdxKeyPair : fdxKeyPairs){
        fdxProcessorRegistry.put(fdxKeyPair, beanName);
      }
    }
}

最后

这种优雅的套路,就是将显示的if else 转换成一个 通过 注册中心 路由的结构。注册中心维护 “条件”“业务逻辑” 的映射,在注册的过程中充分利用了Spring提供的一些生命周期接口。

当然还有其他的一些变种,看具体情况而定,比如一些小的分支,我们可以路由到方法级别,而不是上面bean级别。实现上可以通过在方法上打annotation,然后通过反射的机制去调用。具体不细说了,有兴趣可以通过博客上留的联系方式和我交流。尼玛,说的好像有人看一下,我就是纯粹记笔记。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Bulletproof Web Design

Bulletproof Web Design

Dan Cederholm / New Riders Press / 28 July, 2005 / $39.99

No matter how visually appealing or packed with content a Web site is, it isn't succeeding if it's not reaching the widest possible audience. Designers who get this guide can be assured their Web site......一起来看看 《Bulletproof Web Design》 这本书的介绍吧!

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具