源码下载地址
https://logaaaaa.oss-cn-beijing.aliyuncs.com/com.gtf.zip
什么是策略模式
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
- 1.环境(Context)角色:持有一个Strategy的引用。
- 2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
定义策略接口->实现不同的策略类->利用多态或其他方式调用策略
策略模式应用场景
聚合支付平台
比如搭建聚合支付平台的时候,这时候需要对接很多第三方支付接口,比如支付宝、微信支付、小米支付等。
通过传统if代码判断的,后期的维护性非常差!
public String toPayHtml2(String payCode){
if(payCode.equals("ali_pay")){
return "调用支付宝接口...";
}
if(payCode.equals("xiaomi_pay")){
return "调用小米支付接口";
}
if(payCode.equals("yinlian_pay")){
return "调用银联支付接口...";
}
return "未找到该接口...";
}
这时候可以通过策略模式解决多重if判断问题。
策略模式优缺点
- 优点
算法可以自由切换(高层屏蔽算法,角色自由切换)
避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)
扩展性好(可自由添加取消算法 而不影响整个功能) - 缺点
策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)
所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)
策略模式环境搭建
创建项目名称 springboot_ strategy
简单实现
Maven依赖信息
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<!-- sprinboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
PayStrategy 抽象角色
/**
* 抽象角色
*/
public interface PayStrategy {
/**
* 共同方法行为
* @return
*/
String toPayHtmlt();
}
具体实现角色
/**
* 具体实现角色
*/
public class AliPayStrategy implements PayStrategy {
@Override
public String toPayHtmlt() {
return "调用接口AliPayStrategy";
}
}
package com.gtf.impl;
import com.gtf.strategy.PayStrategy;
public class WxPayStrategy implements PayStrategy {
@Override
public String toPayHtmlt() {
return "调用接口WxPayStrategy";
}
}
策略枚举类
package com.gtf.payEnum;
/**
* 策略枚举类型
*/
public enum PayEnumStrategy {
/**
* 支付宝支付
*/
ALI_PAY("com.gtf.impl.AliPayStrategy"),
/**
* 银联支付 WxPayStrategy
*/
UNION_PAY("com.gtf.impl.WxPayStrategy");
PayEnumStrategy(String className) {
this.setClassName(className);
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
/**
* class完整地址
*/
private String className;
}
简单工厂封装
/**
* 简单工厂
*/
public class StrategyFactory {
//使用策略工厂获取具体策略实现
public static PayStrategy getPayStrategy(String strrategygyTYPE) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//获取具体策略class地址
String className = PayEnumStrategy.valueOf(strrategygyTYPE).getClassName();
Class<PayStrategy> aClass = (Class<PayStrategy>) Class.forName(className);
PayStrategy payStrategy = aClass.newInstance();
return payStrategy;
}
}
上下文执行
public class PayContextStrategy {
/**
* 获取具体策略实现
*/
public String toPayHtml(String payCode) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
if (StringUtils.isEmpty(payCode))
return "null";
//使用策略工厂获取具体策略实现
PayStrategy payStrategy = StrategyFactory.getPayStrategy(payCode);
if (payStrategy == null) {
return "没有获取具体实现";
}
payStrategy.toPayHtmlt();
return payCode;
}
}
数据库实现
删除策略工厂StrategyFactory,以及枚举类
添加配置文件
###服务启动端口号
server:
port: 8080
spring:
###数据库相关连接
datasource:
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/design_pattern?useUnicode=true&characterEncoding=UTF-8
####打印MyBatias日志
logging:
level:
### 开发环境使用DEBUG 生产环境info或者error
com.gtf.member.mapper: DEBUG
添加sql文件
注意:使用sql文件的时候,要注意类名小写,并将该类注入到spring容器管理,类名加上@Component 注解
/*
Navicat Premium Data Transfer
Source Server : mac本机mysql5.7
Source Server Type : MySQL
Source Server Version : 50720
Source Host : localhost:3306
Source Schema : design_pattern
Target Server Type : MySQL
Target Server Version : 50720
File Encoding : 65001
Date: 06/06/2022 11:13:44
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for payment_channel_copy1
-- ----------------------------
DROP TABLE IF EXISTS `payment_channel_copy1`;
CREATE TABLE `payment_channel_copy1` (
`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`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='支付渠道 ';
-- ----------------------------
-- Records of payment_channel_copy1
-- ----------------------------
BEGIN;
INSERT INTO `payment_channel_copy1` VALUES (4, '支付宝渠道', 'ali_pay', 'aliPayStrategy');
INSERT INTO `payment_channel_copy1` VALUES (5, '小米支付渠道', 'xiaomi_pay', 'wxPayStrategy');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
添加mapper接口以及实体类
public interface PaymentChannelMapper {
@Select("SELECT id as id ,CHANNEL_NAME as CHANNELNAME ,CHANNEL_ID as CHANNELID,strategy_bean_id AS strategybeanid FROM payment_channel where CHANNEL_ID=#{payCode}")
public PaymentChannelEntity getPaymentChannel(String payCode);
}
@Data
public class PaymentChannelEntity {
/** ID */
private Integer id;
/** 渠道名称 */
private String channelName;
/** 渠道ID */
private String channelId;
/**
* 策略执行beanId
*/
private String strategyBeanId;
}
修改上下文接口
@Component
public class PayContextStrategy {
@Autowired
private PaymentChannelMapper paymentChannelMapper;
/**
* 获取具体策略实现
*/
public String toPayHtml(String payCode) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
if (StringUtils.isEmpty(payCode))
return "null";
//查询数据库获取具体的实现
PaymentChannelEntity payCode1 = paymentChannelMapper.getPaymentChannel(payCode);
if (payCode1 == null) {
return "没有查询到";
}
String strategyBeanId = payCode1.getStrategyBeanId();
PayStrategy bean = SpringUtils.getBean(strategyBeanId, PayStrategy.class);
return bean.toPayHtmlt();
}
}
spring上下文获取bean
package com.gtf.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
spring当中使用策略模式
new ClassPathXmlApplicationContext 传递资源.xml
Spring 为 Resource 接口提供了如下实现类:
UrlResource:访问网络资源的实现类。
ClassPathResource:访问类加载路径里资源的实现类。
FileSystemResource:访问文件系统里资源的实现类。
ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
InputStreamResource:访问输入流资源的实现类。
ByteArrayResource:访问字节数组资源的实现类。