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,