经典设计模式之策略模式【重构聚合支付平台,对接 (支付宝、微信、银联支付) 】

1、为什么要使用设计模式

使用设计模式可以重构整体架构代码、提交代码复用性、扩展性、减少代码冗余问题。Java高级工程师必备的技能!

2、什么是策略模式

        策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。

1、环境(Context)角色:持有一个Strategy的引用。

2、抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

3、具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

定义策略接口->实现不同的策略类->利用多态或其他方式调用策略。

3、策略模式应用场景

在搭建聚合支付平台时,需要对接很多第三方的支付接口,比如支付宝支付、微信支付、银联支付等,如果使用传统的if判断代码,这对于后期的项目维护性是非常差的。

public String toPayHtml(String payCode){if(payCode.equals("ali_pay")){return  "调用支付宝接口...";}if(payCode.equals("union_pay")){return  "调用银联支付接口";}if(payCode.equals("weChat_pay")){return  "调用微信支付接口...";}return  "未找到该接口...";
}

所以在这个时候就可以使用策略模式来解决这样的多重if判断问题。

4、策略模式架构图

5、策略模式环境搭建

  • maven依赖信息

/*** @Classname PayStrategy* @Description 共同算法的定义骨架* @Date 2019/5/14 15:01* @Created by mark*/
public interface PayStrategy {String toPayHtml();
}
  • ConcreteStrategy (具体实现角色)

/*** @Classname AliPayStrategy* @Description 支付宝支付* @Date 2019/5/14 15:04* @Created by mark*/
@Component
public class AliPayStrategy implements PayStrategy {@Overridepublic String toPayHtml() {return "调用支付宝支付接口...";}
}
/*** @Classname UnionPayStrategy* @Description 银联支付* @Date 2019/5/14 15:05* @Created by mark*/
@Component
public class UnionPayStrategy implements PayStrategy {@Overridepublic String toPayHtml() {return "调用银联支付接口...";}
}
/*** @Classname WeChatPayStrategy* @Description 微信支付* @Date 2019/5/14 15:04* @Created by mark*/
@Component
public class WeChatPayStrategy implements PayStrategy {@Overridepublic String toPayHtml() {return "调用微信支付接口...";}
}
  • PayContextStrategy (上下文)

/*** @Classname PayContextStrategy* @Description 上下文* @Date 2019/5/14 15:06* @Created by mark*/
@Component
public class PayContextStrategy {@Autowiredprivate PaymentChannelMapper paymentChannelMapper;public String toPayHtml(String payCode){//1、参数验证if(StringUtils.isEmpty(payCode)){return BaseReturnInfo.PAYCODE_IS_BLANK;}//2、使用payCode查询PaymentChannelEntity paymentChannel = paymentChannelMapper.getPaymentChannel(payCode);if(paymentChannel == null){return BaseReturnInfo.PAYMENTCHANNEL_IS_NULL;}//3、获取策略的beanidString strategyBeanId = paymentChannel.getStrategyBeanId();if (StringUtils.isEmpty(strategyBeanId)) {return BaseReturnInfo.STRATEGYBEANID_IS_BLANK;}//4、根据beanid在spring容器中查找到对应的beanPayStrategy payStrategy = SpringUtils.getBean(strategyBeanId, PayStrategy.class);//5、执行具体的策略return payStrategy.toPayHtml();}
}
  • SpringUtils

@Component
public class SpringUtils implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}//获取applicationContextpublic static ApplicationContext getApplicationContext() {return applicationContext;}//通过name获取 Bean.public static Object getBean(String name){return getApplicationContext().getBean(name);}//通过class获取Bean.public static  T getBean(Class clazz){return getApplicationContext().getBean(clazz);}//通过name,以及Clazz返回指定的Beanpublic static  T getBean(String name,Class clazz){return getApplicationContext().getBean(name, clazz);}}
  • BaseReturnInfo

/*** @Classname BaseReturnInfo* @Description TODO* @Date 2019/5/14 15:29* @Created by mark*/
public final class BaseReturnInfo {public static final String  PAYMENTCHANNEL_IS_NULL="没有该渠道信息";public static final String STRATEGYBEANID_IS_BLANK="该渠道没有配置beanid";public static final String PAYCODE_IS_BLANK="渠道code不能为空";
}
  • 数据库访问层

sql脚本:

/*
Navicat MySQL Data TransferSource Server         : localhost
Source Server Version : 50539
Source Host           : localhost:3306
Source Database       : design_patternTarget Server Type    : MYSQL
Target Server Version : 50539
File Encoding         : 65001Date: 2019-05-14 16:30:37
*/SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for payment_channel
-- ----------------------------
DROP TABLE IF EXISTS `payment_channel`;
CREATE TABLE `payment_channel` (`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',`CHANNEL_NAME` varchar(32) NOT NULL COMMENT '渠道名称',`CHANNEL_ID` varchar(32) NOT NULL COMMENT '渠道ID',`strategy_bean_id` varchar(255) DEFAULT NULL COMMENT '策略执行beanid',PRIMARY KEY (`ID`,`CHANNEL_ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='支付渠道 ';-- ----------------------------
-- Records of payment_channel
-- ----------------------------
INSERT INTO `payment_channel` VALUES ('4', '支付宝渠道', 'ali_pay', 'aliPayStrategy');
INSERT INTO `payment_channel` VALUES ('5', '银联支付渠道', 'union_pay', 'unionPayStrategy');
INSERT INTO `payment_channel` VALUES ('6', '微信支付渠道', 'wechat_pay', 'weChatPayStrategy');
  • Entity

@Data
public class PaymentChannelEntity {/** ID */private Integer id;/** 渠道名称 */private String channelName;/** 渠道ID */private String channelId;/*** 策略执行beanId*/private String strategyBeanId;
}
  • Mapper

public interface PaymentChannelMapper {@Select("SELECT  id as id ,CHANNEL_NAME as CHANNELNAME ,CHANNEL_ID as CHANNELID,strategy_bean_id AS strategybeanid\n" +"FROM payment_channel where CHANNEL_ID=#{payCode}")PaymentChannelEntity getPaymentChannel(String payCode);
}
  • controller

@RestController
public class PayController {@Autowiredprivate PayContextStrategy payContextStrategy;@RequestMapping("/toPayHtml")public String toPayHtml(String payCode) {return  payContextStrategy.toPayHtml(payCode);}
}
  • application.yml

/*** @Classname StrategyApp* @Description TODO* @Date 2019/5/14 15:00* @Created by mark*/
@SpringBootApplication
@MapperScan("com.xwhy.mapper")
public class StrategyApp {public static void main(String[] args) {SpringApplication.run(StrategyApp.class,args);}
}
  • 启动效果截图:

6、总结

优点

    算法可以自由切换(高层屏蔽算法,角色自由切换)

    避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)

    扩展性好(可自由添加取消算法 而不影响整个功能)

缺点

    策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)

    所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部