[代码结构设计]根据不一样条件使用不一样实现类的业务代码设计

场景

此时有一个场景,须要设计一个根据不一样的状态和条件采用不一样的业务处理方式。java

这样你们可能不是太理解。举个例子,如今大街小巷上的商户都采用了聚合支付的支付方式,聚合支付也就是商户柜台前放了一个支持支付宝、微信、京东钱包、银联等等的二维码,用户能够经过任意一款支付APP进行支付。git

解决思路

思路①

对每一个支付渠道进行定义枚举类型github

public enum PayWay {
    ALI_PAY,

    WECHAT_PAY;
}
复制代码

而后在每一个对应的service上定义注解,表示对应哪一种支付方式spring

@Pay(PayWay.ALI_PAY)
public class AliPayServiceImpl implements PayService {}
复制代码

可是仔细思考后,仍是存在一些问题springboot

  1. 若是增长一个支付方式后还须要修改,PayWay这个枚举类型
  2. 在程序中,仍须要根据不一样的条件作if else判断PayWay,增长支付方式仍是得修改原有的判断逻辑。伪代码以下
if("xxx" == "aliPay"){
    
} else if("xxx" == "wechatPay"){
    
}
//若是增长支付方式仍是得增长else if
复制代码

思路②

在思路①中存在一些问题,首当其冲的就是if else判断问题。先思考一下这个if else的做用是什么?bash

答:根据思路①描述,这个if else是用来肯定采用哪一种支付方式。微信

咱们能够将这块代码抽离出来,让对应的业务实现类实现本身的逻辑实现,而后根据返回值true 或者false决定是否过滤掉这个业务实现类。接口定义以下,SupportBean是封装的一个实体框架

boolean isSupport(SupportBean supportBean);
复制代码

而后在各个业务实现类都实现本身的isSupport方法,伪代码以下ide

@Override
public boolean isSupport(SupportBean supportBean) {
    if (supportBean.getType() == "xxx"){
        return true;
    }
    
    return false;
}
复制代码

设计

注:只提供一个架子工具

接口定义

Service接口定义,一个业务执行方法execute(参数自行添加),一个isSupport方法(返回true或者false

public interface Service {

    void execute();

    boolean isSupport(SupportBean supportBean);
}
复制代码

业务实现类

这里execute方法只是在控制台打印字符串。isSupport方法对SupportBean中的supportNum进行取余,判断余数是否等于0,是则返回true。

相似的实现还有两个,这里就不贴出来了。

@Component
public class AServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("A execute");
    }

    @Override
    public boolean isSupport(SupportBean supportBean) {
        return supportBean.getSupportNum() % 3 == 0;
    }
}
复制代码

接下来在定义一个帮助类

帮助类

@Component
public class Helper {

    @Autowired
    private List<Service> services;

    public void execute(SupportBean supportBean){

        Service s = services.stream()
                .filter((service) -> service.isSupport(supportBean))
                .findFirst()//NPE异常
                .orElse(null);


        if (s != null){
            s.execute();
        }
    }
}
复制代码

经过工具类的execute方法来获取对应的业务实现类执行的结果,以及对传入的参数进行校验处理等。

须要注意的是Lambda表达式的findFirst()会出现NullPointException异常。由于filter对list进行过滤,会存在过滤完list的长度为0,若是此时在调用findFirst则会抛出NullPointException。能够将上面的代码修改成以下代码,这样就能够避免NPE了

Service s = services.stream()
        .filter((service) -> service.isSupport(supportBean))
        .map(Optional::ofNullable)
        .findFirst()
        .flatMap(Function.identity())
        .orElse(null);
复制代码

测试

添加一个springboot测试类和一个测试方法。

在contextLoads测试中调用帮助类Helper的execute方法

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

    @Autowired
    private Helper Helper;

    @Test
    public void contextLoads() {
        Helper.execute(new SupportBean(3));
    }

}
复制代码

测试结果

A execute
复制代码

扩展

在Lambda表达式中是先将业务实现类进行过滤,而后获取第一个业务实现类并执行。

若是此时过滤存在多个业务实现类,而又不能肯定优先级,这时须要如何进行扩展呢?

其实很简单,先在Service接口中定义一个getPriority方法

int getPriority();
复制代码

而后各自的实现类实现对应的getPriority方法

接着修改Lambda表达式便可,在filter后增长sorted方法便可对业务实现类进行排序

Service s = services.stream()
        .filter((service) -> service.isSupport(supportBean))
        .sorted(Comparator.comparing(Service::getPriority))
        .map(Optional::ofNullable)
        .findFirst()
        .flatMap(Function.identity())
        .orElse(null);
复制代码

总结

整个大致框架基本都搭建完成,如需扩展只须要增长对应的业务实现类,而不用去修改其余类的代码。就连以前设计的枚举均可以不用,可扩展性大大提高。如需使用,只需修改对应的入参和对应的名称便可。 Github地址 若是对你有收获,欢迎star、欢迎fork 若是你也有相似的经验,欢迎加入,一块儿共建