前言
相信大家经常会遇到这种需求,产品经理开始为了快速推广某业务需要有个需求紧急上线,一般开发接收的思路都是这样的,先实现这个功能,这样看起来也确实没有问题,但是随着业务的发展以及用户的增加,后续会有不同的产品提出各种各样的新需求,这样就会出现一个问题大家都在同一个地方修改代码逻辑,久而久之代码就会成为一座大山,留给接手的人吐槽。
适配器模式是GoF23设计模式之一,引用度娘原话:适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。出处:
实际应用场景
未使用设计模式:
某业务场景产品要求对接下京东支付做在线支付逻辑处理,张三同学收到需求后产品要求说两天后上线(研发内心是奔溃的),但是好在总体来说逻辑比较单一,张三同学很快的完成了以下逻辑,代码逻辑如下。
public class PayHandle {
public boolean pay(String payType,String orderId) {
//京东支付
if("1".equals(payType)) {
//具体的业务处理逻辑
return jdPay(orderId);
}
return Boolean.FALSE;
}
public boolean jdPay(String orderId) {
return Boolean.TRUE;
}
}
项目很快的上线了,这样过了一个月,产品经理说业务发展的比较火爆,业务要求对接新的支付方式,李四接收到需求后看了下,这不就是加个判断加个方法么?新的2.0版本来了
public class PayHandle {
public boolean pay(String payType,String orderId) {
//京东支付
if("1".equals(payType)) {
//具体的业务处理逻辑
return jdPay(orderId);
}
//支付保
else if("2".equals(payType)) {
return aliPay(orderId);
}
return Boolean.FALSE;
}
public boolean jdPay(String orderId) {
return Boolean.TRUE;
}
public boolean aliPay(String orderId) {
return Boolean.TRUE;
}
}
项目经过N个版本的迭代后出现了一个问题,大家在上面写的业务逻辑越来越多,最终出现了修改后上线产生了生产事故,这样的问题相信在很多业务场景中有其他业务有类似的问题;究其原因主要是项目迭代的过快而且没有好的设计导致的。
使用设计模式:
接着大家有人就坐不住了,觉得设计模式有什么用,不也得堆代码么;其实并不是的,看过设计模式六大原则的人懂得都懂,如果有不了解设计模式六大原则的可以先移步熟悉设计模式概念:
代码优化版本
这里使用到了大家比较熟知的Spring框架。
统一支付接口:
public interface PayHandleService {
/**
* @Title: pay
* @Description: 支付方法
* @param payType 支付类型(可使用支付系统统一封装为对应枚举提供大家使用)
* @param orderId 支付订单号
* @version V1.0
*/
public boolean pay(String payType,String orderId);
}
京东支付接口实现类
@Service
public class JdPayHandleServiceImpl implements PayHandleService {
/**获取支付方式*/
@Override
public String getPayType() {
return "1";
}
/**
* 京东支付处理方法
*/
@Override
public boolean pay(String payType, String orderId) {
//具体处理业务逻辑
return false;
}
}
支付保支付接口实现
@Service
public class AliPayHandleServiceImpl implements PayHandleService {
/**获取支付方式*/
@Override
public String getPayType() {
return "2";
}
/**支付类型*/
@Override
public boolean pay(String payType, String orderId) {
//处理具体逻辑
return false;
}
}
使用Autowired注入实现处理类
@Service
public class PayHandle{
@Autowired
private Map<String, PayHandleService> payHandleServiceMap;
public boolean pay(String payType,String orderId) {
for (Entry<String, PayHandleService> entry:payHandleServiceMap.entrySet()) {
PayHandleService payHandleService = entry.getValue();
if(payType.equals(payHandleService.getPayType())) {
return payHandleService.pay(payType, orderId);
}
}
return Boolean.FALSE;
}
}
使用ApplicationContextAware回调注入处理
@Service
public class PayHandle implements ApplicationContextAware{
private Map<String, PayHandleService> payHandleServiceMap;
public boolean pay(String payType,String orderId) {
for (Entry<String, PayHandleService> entry:payHandleServiceMap.entrySet()) {
PayHandleService payHandleService = entry.getValue();
if(payType.equals(payHandleService.getPayType())) {
return payHandleService.pay(payType, orderId);
}
}
return Boolean.FALSE;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
payHandleServiceMap = applicationContext.getBeansOfType(PayHandleService.class);
}
}
这样实现似乎还不太优雅,大家发现不管是用Autowired 注入还是使用ApplicationContextAware 回调注入最终获取到的都是一个map,这个map的key其实就是具体实现类的名称,我们都知道Spring默认实现类如果不指定id则只有当前bean的name首字母小写,这样的话其实还可以这样做。
优化后的新增枚举
public enum PayTypeMapping {
JD_PAY("1","jdPayHandleServiceImpl","京东支付"),
ALI_PAY("2","aliPayHandleServiceImpl","支付保支付"),
;
/**支付类型*/
private String payType;
/**支付类型实现类名称*/
private String payHandleName;
/**描述*/
private String desc;
public String getPayType() {
return payType;
}
public String getPayHandleName() {
return payHandleName;
}
public String getDesc() {
return desc;
}
private PayTypeMapping(String payType, String payHandleName, String desc) {
this.payType = payType;
this.payHandleName = payHandleName;
this.desc = desc;
}
private static final Map<String, PayTypeMapping> CODE_MAP = EnumSet.allOf(PayTypeMapping.class).stream().collect(Collectors.toMap(PayTypeMapping::getPayType, a -> a, (k1, k2) -> k1));
/**根据支付类型获取支付实现类相关信息*/
public static PayTypeMapping of(String code) {
return StringUtils.isNotBlank(code)? CODE_MAP.get(code) : null;
}
}
优化后的最终调用逻辑
@Service
public class PayHandle{
@Autowired
private Map<String, PayHandleService> payHandleServiceMap;
public boolean pay(String payType,String orderId) {
PayTypeMapping payTypeMapping = PayTypeMapping.of(payType);
if(Objects.nonNull(payTypeMapping)) {
PayHandleService payHandleService = payHandleServiceMap.get(payTypeMapping.getPayHandleName());
return payHandleService.pay(payType, orderId);
}
return Boolean.FALSE;
}
}
结语
最终优化有后续不管新增什么样的类型都是单独修改不会影响到其他的业务逻辑,本文只是列举了一种支付的行为,其实还有好多种实际业务场景也可以使用适配器解决代码的臃肿以及可维护性,最后还是要提醒大家设计模式也不是任何场景都适用,切记不要邯郸学步生硬的将设计模式加入到实际项目中,这样其实也会导致适得其反;加入设计模式的初衷不是为了体现代码有多么高大上,最终的目的还是保障项目的可维护性和可扩展性;也希望大家读完这篇文章对自己的知识储备有了新的认知。