策略+工厂模式两招教你在Spring中写出优美的业务代码

JavaEdge JavaEdge 2023-03-03 21:24 发表于上海

收录于合集#设计模式18个

转自大佬的文章~

大家好,我是小白技术圈的猿小白。

背景

问问大家,在你们的工作当中,有没有接手过“祖传代码”?也就是老板让你维护前一任程序员写的老项目。

猿小白是个工作多年的老程序员,有过很多次这样的经历。这些个祖传代码,有相当一部分压根儿不知道写的是什么,注释非常少,结构也很混乱,不敢修改也不敢删除。

当初写下这些代码的前任,要么已经离职了,根本找不到人来问。

就算他还在职,能找到人,想问对方一点代码的问题,也还得看对方的脸色。

这就是祖传代码的痛苦。

在实际开发中,我们会经常遇到不同的业务类型对应不同的业务处理,而这个业务类型又是经常变动的。

举个猿小白在实际开发中碰到的例子,比如说,我们在做支付对接业务的时候,可能刚开始需要实现支付宝支付和微信支付,那么代码逻辑可能如下。

    /**
     * 支付选择简易逻辑
     *
     * @param payType payType zfb-支付宝支付,wx-微信支付
     * @param money   需要支付的钱
     */
    public void pay(String payType, Double money) {
        if ("zfb".equals(payType)) {
            System.out.println("=======执行支付宝支付========");
        } else if ("wx".equals(payType)) {
            System.out.println("=======执行支微信支付========");
        } else {
            System.out.println("=======支付类型错误========");
        }
    }

真实的代码比这复杂多了,一层又一层的if else嵌套,看得你头皮发麻。

这时产品经理来了个新需求,需要你来对接云闪付支付,那么我们又要else if ().....

如果哪一天我们又要......

这样的话,我们这个类会随着这支付类型的变动不断慢慢的扩展和修改....

在修改的过程中甚至将原来的弄错......

最后总结这样的代码违反了开闭原则,代码设计思想应该是对修改关闭,对扩展开放。

我们该怎么办呢?

许多前辈程序员经过长期实践,总结出了一系列的解决方案。这些解决方案可以提高代码的可读性,增加代码的可重用性,保证代码的可扩展性。

这一系列解决方案,被人们称为设计模式,它是面向对象编程当中的各种经典套路。

开始设计模式的表演

猿小白结合自己的工作经验,给出自己的见解。

这里我们使用spring官方的start,快速构建一个demo项目。

https://start.spring.io/

包结构

首先看看猿小白的代码结构,猿小白习惯将一个领域的代码放在同一个包下,比如这次的payment

  • controller  http接口

  • service  服务接口

    • constants 常量类放在这里

    • impl 实现类

枚举定义

接着,我们定义好项目支持的支付枚举类型。这里我们先支持支付宝、微信两种。

/**
 * 支付类型
 */
public enum PayTypeEnum {
    /**
     * 支付宝
     */
    ZFB("zfb","支付宝"),

    /**
     * 微信
     */
    WX("wx","微信")

    ;

    PayTypeEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public static PayTypeEnum getPayTypeByCode(String code){
        for (PayTypeEnum e : PayTypeEnum.values()){
            if (e.getCode().equals(code)){
                return e;
            }
        }

        return null;
    }
}

接口服务定义

然后,我们定义好订单服务的接口,入参主要是支付类型、支付金额。

/**
 * 订单服务
 */
public interface OrderService {

    /**
     * 订单支付
     * @param payType 支付类型
     * @param money 支付金额
     */
    void payOrder(PayTypeEnum payType, Double money);
}

接着,我们定义好支付服务的接口。

  • pay 支付方法,入参是金额

  • payType 返回支付类型

/**
 * 支付服务
 */
public interface PayService {

    /**
     * 支付
     * @param money
     */
    void pay(Double money);

    /**
     * 支付类型
     * @return
     */
    PayTypeEnum payType();

}

实现支付服务

重点的地方来了,我们先实现支付宝的支付对接。

  • pay方法,根据实际的对接接口编写

  • payType方法,这里就返回定义好的支付宝枚举

/**
 * 支付宝支付
 */
@Service
public class ZfbPayServiceImpl implements PayService {
    /**
     * 支付
     *
     * @param money
     */
    @Override
    public void pay(Double money) {
        System.out.println("支付宝支付:"+money + "元");
    }

    /**
     * 支付类型
     *
     * @return
     */
    @Override
    public PayTypeEnum payType() {
        return PayTypeEnum.ZFB;
    }
}

接着我们实现微信的支付对接。

  • pay方法,根据实际的对接接口编写

  • payType方法,这里就返回定义好的微信枚举

@Service
public class WxPayServiceImpl implements PayService {
    /**
     * 支付
     *
     * @param money
     */
    @Override
    public void pay(Double money) {
        System.out.println("微信支付:"+money + "元");
    }

    /**
     * 支付类型
     *
     * @return
     */
    @Override
    public PayTypeEnum payType() {
        return PayTypeEnum.WX;
    }
}

实现订单服务

  • payServiceList  标注了@Autowired注解,Spring会自动帮我们把所有的PayService的实现类注入进来。

  • payRouters用来维护所有的支付服务映射关系,其实就是简单的工厂。

  • InitializingBean 是Spring留给我们的bean初始化的钩子,通过实现它的afterPropertiesSet方法,将支付服务的映射关系维护到payRouters中。

  • payOrder实现方法中,通过payRouters工厂传入指定的payType类型,得到具体的PayService服务。

@Service
public class OrderServiceImpl implements OrderService, InitializingBean {

    @Autowired
    private List<PayService> payServiceList;

    private static final Map<PayTypeEnum, PayService> payRouters = new ConcurrentHashMap<>();

    /**
     * 订单支付
     *
     * @param payType
     * @param money
     */
    @Override
    public void payOrder(PayTypeEnum payType, Double money) {
        if (payType == null){
            throw new RuntimeException("支付类型为空");
        }
        PayService payService = payRouters.get(payType);
        payService.pay(money);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        for (PayService service : payServiceList){
            payRouters.put(service.payType(), service);
        }
    }
}

定义接口controller

/**
 * 订单
 */
@RestController
@RequestMapping("/api/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/payOrder")
    public String payOrder(@RequestParam String payType , @RequestParam Double money){

        PayTypeEnum payTypeEnum = PayTypeEnum.getPayTypeByCode(payType);
        if (payTypeEnum == null){
            return "不支持当前的支付方式";
        }

        orderService.payOrder(payTypeEnum,money);
        return String.format("使用%s支付成功%.2f元",payTypeEnum.getMessage() , money);
    }

}

看看效果

使用支付包支付10.1元

使用微信支付5.63元

这时候去使用云闪付支付,我们还没有实现,所以不支持。

实现新需求-对接云闪付

这时候,我们需要实现新需求,对接云闪付。我们只需要在PayTypeEnum,再定义一个枚举类型。

    /**
     * 云闪付
     */
    YSF("ysf","云闪付")

再新增一个云闪付的实现类就行了。

这样我们通过策略+工厂模式,保持了修改关闭,对扩展开放。

/**
 * 云闪付
 */
@Service
public class YsfPayServiceImpl implements PayService {
    /**
     * 支付
     *
     * @param money
     */
    @Override
    public void pay(Double money) {
        System.out.println("云闪付支付:"+money + "元");
    }

    /**
     * 支付类型
     *
     * @return
     */
    @Override
    public PayTypeEnum payType() {
        return PayTypeEnum.YSF;
    }
}

好了,新的扩展对接就实现了,简单清晰吧。

小结

本文主要围绕在 Spring引入策略+工厂模式的设计思路和实践方法进行介绍,在实际的业务开发中,合理的使用策略模式,能让代码看起来更加清爽,业务扩展性也更加强大,希望能帮助到大家!

Java程序员加餐福利:

最近整理一份BAT面试资料,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

 

 

领取方式:

扫描下方公众号【小白技术圈】回复:BAT

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值