设计模式的思想:
定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
是基于继承的代码复用的基本技术,没有关联关系。 因此,在模板方法模式的类结构图中,只有继承关系。
核心设计要点:
AbstractClass : 抽象类,定义并实现一个模板方法。这个模板方法定义了算法的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类去实现
ConcreteClass : 实现父类所定义的一个或多个抽象方法。
看了上面的思想和要点,可能依旧不能理解(主要是这个骨架很抽象,很模糊,哈哈哈),下面就拿一个具体案例进行说明:
一个通俗易懂的解释就是对有相似功能(行为)的代码进行提炼,将公共的代码方法提出来放到父类中(抽象类),将相同方法操作,但是具体实现又不同的代码 就抽成抽象方法,让子类进行实现。再就是核心点(骨架),以下示例就用来很好的解释骨架这个东西。
以一个聚合支付接口回调的行为作为例子进行讲解。 在我们开发过程中,经常碰到需要对接第三方接口,而且这个第三方存在多厂商的区别,比如支付宝、微信、银联、嘉联等支付厂家。那么在我们对接支付时,各个厂家的回调以及调用接口都有所不同,但是操作流程(这个步骤也就是我们所说的骨架)是一致的。
话不多说,直接上代码(需要自己创建springboot项目,之后再将下面代码拷入对应目录即可):
项目结构:
0.maven依赖(dependencies部分)
<properties>
<java.version>1.8</java.version>
<spring-boot-version>2.0.7.RELEASE</spring-boot-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
</dependencies>
1、我们的抽象父类 AbstractPayCallBackTemplate (*****重点核心)
@Slf4j
public abstract class AbstractPayCallBackTemplate {
/**
* 定义共同行为的骨架---->也就是整个流程方法 从签名->日志->签名状态->支付成功修改订单
*
* @return
*/
public String asyncCallBack() {
log.info(">>>>回调流程准备开始调用");
//1.验证参数和验证签名
Map<String, String> verifySignature = verifySignature();
//2.日志收集 相同行为
payLog(verifySignature);
//3.获取签名状态
String analysisCode = verifySignature.get("analysisCode");
if (!analysisCode.equals("200")) {
return resultFail();
}
//4.更新数据库状态同时返回不同支付结果
return asyncService(verifySignature);
}
/**
* 返回失败结果
* 对应不同的支付方式,失败返回结果不同,故抽象出来,子类自行实现
*
* @return
*/
protected abstract String resultFail();
/**
* 返回成功结果
* 对应不同的支付方式,成功返回结果不同,故抽象出来,子类自行实现
*
* @return
*/
protected abstract String resultSuccess();
/**
* 验证参数 ...
* 对应不同的支付方式,验证签名不同,故抽象出来,子类自行实现
*
* @return
*/
protected abstract Map<String, String> verifySignature();
/**
* 多线程异步写入日志 也可以使用MQ
* 日志记录都是相同的公共方法,父类公用
*
* @param verifySignature
*/
@Async
public void payLog(Map<String, String> verifySignature) {
log.info("第二步>>>>>写入日志信息....verifySignature:{}", verifySignature);
}
/**
* 执行修改订单状态 和 返回不同支付结果
* 具体根据接口返回状态,来进行具体数据库操作及返回结果
*
* @param verifySignature
* @return
*/
protected abstract String asyncService(Map<String, String> verifySignature);
}
2、具体业务实现类 AliPayCallBackTemplate 阿里回调模板实现类
@Slf4j
@Component
public class AliPayCallBackTemplate extends AbstractPayCallBackTemplate {
@Override
protected String resultFail() {
return "fail";
}
@Override
protected String resultSuccess() {
return "success";
}
@Override
protected Map<String, String> verifySignature() {
log.info(">>>>>第一步 进入阿里支付签名验证....verifySignature()");
Map<String, String> verifySignature = new HashMap<>();
verifySignature.put("price", "1333");
verifySignature.put("orderDes", "充值天猫会员");
//>>>>假设阿里pay回调报文状态 1 表示 success
verifySignature.put("aliPayMentStatus", "1");
verifySignature.put("aliPayOrderNumber", "20210722101010");
//>>>>假设阿里pay回调报文结束>>>>>
//解析报文是否成功 或者签名成功返回 200
verifySignature.put("analysisCode", "200");
return verifySignature;
}
@Override
protected String asyncService(Map<String, String> verifySignature) {
log.info(">>>>>第三步 阿里 asyncService() verifySignatureMap:{}", verifySignature);
String aliPayMentStatus = verifySignature.get("aliPayMentStatus");
String aliPayOrderNumber = verifySignature.get("aliPayOrderNumber");
if (aliPayMentStatus.equals("1")) {
log.info(">>>>>orderNumber:{},已经支付成功,修改订单状态为已支付....", aliPayOrderNumber);
return resultSuccess();
}
return resultFail();
}
}
微信支付回调模板实现类 WeChatPayCallBackTemplate
@Slf4j
@Component
public class WeChatPayCallBackTemplate extends AbstractPayCallBackTemplate {
@Override
protected String resultFail() {
return "fail";
}
@Override
protected String resultSuccess() {
return "ok";
}
@Override
protected Map<String, String> verifySignature() {
log.info(">>>>>进入微信支付签名验证....verifySignature()");
Map<String, String> verifySignature = new HashMap<>();
verifySignature.put("price", "1366");
verifySignature.put("orderDes", "充值QQ会员");
//>>>>假设阿里pay回调报文状态 1 表示 success
verifySignature.put("weChatPayMentStatus", "1");
verifySignature.put("weChatPayOrderNumber", "20220722101022");
//>>>>假设阿里pay回调报文结束>>>>>
//解析报文是否成功 或者签名成功返回 200
verifySignature.put("analysisCode", "200");
return verifySignature;
}
@Override
protected String asyncService(Map<String, String> verifySignature) {
log.info(">>>>>第三步 微信 asyncService() verifySignatureMap:{}", verifySignature);
String weChatPayMentStatus = verifySignature.get("weChatPayMentStatus");
String weChatPayOrderNumber = verifySignature.get("weChatPayOrderNumber");
if (weChatPayMentStatus.equals("1")) {
log.info(">>>>>orderNumber:{},已经支付成功,修改订单状态为已支付....", weChatPayOrderNumber);
return resultSuccess();
}
return resultFail();
}
}
3、模板工厂类 TemplateFactory
public class TemplateFactory {
/**
* 使用工厂模式获取模板对象
* 可通过枚举进行参数优化,避免bean名称暴露
* @param templateId
* @return
*/
public static AbstractPayCallBackTemplate getPayCallBackTemplate(String templateId) {
//使用spring容器进行获取实例
AbstractPayCallBackTemplate payCallBackTemplate = (AbstractPayCallBackTemplate) SpringUtil.getBean(templateId);
return payCallBackTemplate;
}
}
4、spring容器工具类 SpringUtil
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.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);
}
}
5、接口类 TemplateController
@RestController
public class TemplateController {
@RequestMapping("asyncCallBack")
public String asyncCallBack(String templateId) {
AbstractPayCallBackTemplate payCallBackTemplate = TemplateFactory.getPayCallBackTemplate(templateId);
//
return payCallBackTemplate.asyncCallBack();
}
}
6、springboot项目启动类
@SpringBootApplication
@EnableAsync
public class DesignerModeApplication {
public static void main(String[] args) {
SpringApplication.run(DesignerModeApplication.class, args);
}
}
进行测试:
发送请求: http://127.0.0.1:8080/asyncCallBack?templateId=weChatPayCallBackTemplate
发送请求:http://127.0.0.1:8080/asyncCallBack?templateId=aliPayCallBackTemplate
对应两次执行的日志打印
总结:
模板方法设计模式,主要可以对代码的复用。
与策略模式的主要区别在于:策略模式时不同的行为骨架,主要减少多重if判断。
模板方法设计模式主要在于:提前定义共同行为的骨架, 相同行为放在抽象类实现(公用), 不同行为放到子类实现。
鼓励下自己:只有不断学习,才能不断成长,加油,坚持。