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

场景

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

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

解决思路

思路①

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

public enum PayWay {
    ALI_PAY,

    WECHAT_PAY;
}

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

@Pay(PayWay.ALI_PAY)
public class AliPayServiceImpl implements PayService  {}

但是仔细思考后,还是存在一些问题

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

思路②

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

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

我们可以将这块代码抽离出来,让对应的业务实现类实现自己的逻辑实现,然后根据返回值true 或者false决定是否过滤掉这个业务实现类。接口定义如下,SupportBean是封装的一个实体

boolean isSupport(SupportBean supportBean);

然后在各个业务实现类都实现自己的isSupport方法,伪代码如下

@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
如果你也有类似的经验,欢迎加入,一起共建

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,090评论 25 708
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,864评论 2 59
  • 亲爱的自己,大雪下了一天一夜,早上起来,只看见外面是一个银装素裹的世界。这场大雪给我们的出行带来了很多不便。大家都...
    冬季飘雪阅读 105评论 0 1
  • 听完“VVG Chin Chin 开幕讲座”,受益颇深,尤其是和朱平先生对生活理念上达成了很多共识之处,巧妙的是,...
    KJWANG阅读 498评论 0 0
  • 你只是路过我的心间 我却需要一生去忘却 忘不了你在操场上奔跑的样子 忘不了你皱眉头的样子 忘不了春风吹拂你刘海的样...
    阿亮的月亮阅读 237评论 1 5