意图

定义一个操作中的算法的骨架,将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

模板方法模式的诞生

模板方法模式为我们提供了一种代码复用的重要技巧,它定义了算法的步骤,把这些步骤的实现延迟到子类

说人话就是:

【产品】:开发小哥,你会做饭吗?

【开发】:不会啊,怎么了?难道你要做给我吃?

【产品】:你想太多了,我就准备教你做一道炒包菜~,用你们写代码思路,我也来试试写写伪代码

public void cookie(){
// 第一步:倒油
this.pourOil();
// 第二步:热油
this.HeatOil();
// 第三步:倒入包菜
this.pourVegetable();
// 第四步:倒入调味料
this.pourSauce();
// 第五步:翻炒
this.fry();
}

【开发】:我懂了,小姐姐,你要不来我家?我给你做一道炒蒜蓉~

【产品】:???你不是说你不会做饭吗?

【开发】:你不是写出来步骤了嘛,我已经会了,嘿嘿嘿~

你要是还没会,那咱们就一起看看核心代码吧~

你如果会了,那直接跳到SSO实战吧~

HeadFirst 核心代码

抽象类

public abstract class TemplateClass {

	/***
* 模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
* 可根据需求申明为final,防止子类覆盖这个方法,导致流程的执行顺序
*/
final void cookProcess() { // 第一步:倒油
this.pourOil(); // 第二步:热油
this.heatOil(); // 第三步:倒蔬菜
this.pourVegetable(); // 配合钩子函数, 确定是否需要倒调味料
if (needSauce()) {
this.pourSauce();
} // 第五步:翻炒
this.fry();
} void pourOil() {
System.out.println("倒油");
} void heatOil() {
System.out.println("热油");
} /***
* 需要变化的部分就定义为抽象
*/
abstract void pourVegetable();
abstract void pourSauce(); /***
* 钩子函数, 影响方法调用逻辑
*/
boolean needSauce() {
return true;
} void fry() {
System.out.println("炒啊炒啊炒到熟啊");
}
}

子类实现

public class SuanRong extends TemplateClass {

	@Override
boolean needSauce() {
return false;
} @Override
void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
} @Override
void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}

模板方法模式的设计思路:

  • AbstractClass(抽象类)抽象基类,定义抽象方法,方法调用顺序等等
  • ConcreteClass(实现类)继承抽象基类,实现预先定义的方法

简单来说,

  1. 我们需要一个抽象类或者接口(Java8之后接口也可以提供具体的方法),定义需要实现的方法和方法执行逻辑
  2. 根据业务情况继承抽象类或接口,实现特定方法,使多种情况互相隔离
  3. 配合如策略或其他的方式合理的构建需要的实现类即可

如果看着有点模棱两可,建议看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面

模板的力量:5分钟一个SSO

要说到用模板方法模式去使用SSO时,咱们先需要知道什么是SSO(知道的同学直接略过啦~)

SSO

SSO即单点登录,即在多个系统中,用户只需一次登录,各个系统即可感知该用户已经登录

用户鉴权的几种常见方式

本文主要还是侧重设计模式的实战,所以就简单说几句,常见方式如:

  • oauth 2.0
  • cookie

第一个SSO

我们在编写第一个SSO的时候,还是有那么一点费劲的,需要考虑各种流程,细节等等

(这里指的是我们集成其他公司的鉴权系统)

我所在的公司客户技术水平五花八门,技术栈也是五花八门,所以不是标准的oauth2.0结构,大致抽象出以下核心环节,如:

public interface SSOContextAnalysis {

    /** 跳转登录*/
void toLogin(SSOConfig ssoConfig, HttpServletResponse httpServletResponse); /** 获取token */
Object getToken(SSOConfig ssoConfig, HttpServletRequest httpServletRequest); /***
* 获取用户信息
* @param ssoConfig 配置
* @return 用户信息加密串
*/
String getUserInfo(SSOConfig ssoConfig, Object arg); /***
* 解析sso context
*/
String getAccount(SSOConfig ssoConfig, Object userInfo); /** 进行退出 */
boolean tologout(SSOConfig ssoConfig, UserOnline userOnline, HttpServletResponse httpServletResponse);
}

龙图SSO Demo

public class LongTuSSOHandle implements SSOContextAnalysis{

    @Override
public void toLogin(SSOConfig ssoConfig) {
// 跳转SSO鉴权地址, 配置回调地址
} @Override
public Object getToken(SSOConfig ssoConfig) {
// 根据客户的加密算法等获取Token信息
return null;
} @Override
public String getUserInfo(SSOConfig ssoConfig, Object arg) {
// 组装报文, 发起用户请求, 获取用户数据
return null;
} @Override
public String getAccount(SSOConfig ssoConfig, Object userInfo) {
// 基于用户数据, 获取本平台用户数据, 完成SSO流程
return null;
} @Override
public boolean tologout(SSOConfig ssoConfig) {
// 根据退出地址进行退出操作等等
return false;
}
}

我们在编写业务代码的时候,完全依赖于顶层抽象类,彼时再动态的更改具体的实现类即可

如何动态指定实现类

动态指定实现类的最好方式无非这么几种,大家可以根据需求选择:

  • 配置文件 + 反射构建实现类
  • 配置文件 + 预先加载所有实现类,根据配置动态选择
  • 数据库配置 + 反射(可以使用CGLIB代理)
  • 数据库配置 + 预先加载所有实现类,根据配置动态选择

第二个SSO要多久?

当我们使用模板方法模式,对整个SSO的流程已经梳理完成之后,第二个SSO只需要实现相应接口,然后根据客户的要求构建不同的加密协议,同时改一下配置即可,反正我只花了5分钟~

遵循的设计原则

  1. 封装变化

    • 在抽象基类中,我们可以有已经实现的方法供子类调用
    • 在抽象基类中,对于必然不同的方法逻辑,定义为抽象的,供子类自行实现
  2. 好莱坞原则
    • 别找我,我会找你 指的是底层代码(具体的实现类)不依赖于高层代码,我们在本次的SSO实战中也体现了,直接依赖接口,实现类只是作为真正的执行者而已

什么场景适合使用

  • 一次性实现一个算法不变的部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为应该被提取出来并集中到一个公共父类中以避免代码重复
  • 控制子类扩展(文中提到的钩子方法,控制方法的执行与否)

Code/生活中的实际应用

比如我们使用的所有APP,在进行支付的时候大多都可以选择支付宝或者微信支付,其实整个支付步骤中,只有具体支付的步骤是分为多种情况(微信,支付宝,银行卡等)剩下的订单推送,数据扭转等很有可能是一样的,此时我们就可以使用模板方法模式来约束行为,同时减少重复代码

PS:此种情况也可能使用策略模式处理,需要视情况而定

最后

附上GOF一书中对于模板方法模式的UML图:

相关代码链接

GitHub地址

  • 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
  • 提供了友好的阅读指导

【一起学系列】之模板方法:写SSO我只要5分钟的更多相关文章

  1. Asp.Net Core 轻松学系列-1阅读指引目录

    https://www.cnblogs.com/viter/p/10474091.html 目录 前言 1. 从安装到配置 2. 业务实现 3. 日志 4. 测试 5. 缓存使用 6.网络和通讯 7. ...

  2. 目录---Asp.NETCore轻松学系列【目录】

    随笔分类 - Asp.NETCore轻松学系列 Asp.NETCore轻松学系列阅读指引目录 摘要: 耗时两个多月,坚持写这个入门系列文章,就是想给后来者更好更快的上手体验,这个系列可以说是从入门到进 ...

  3. 【目录】Asp.NETCore轻松学系列

    随笔分类 - Asp.NETCore轻松学系列 Asp.NETCore轻松学系列阅读指引目录 摘要: 耗时两个多月,坚持写这个入门系列文章,就是想给后来者更好更快的上手体验,这个系列可以说是从入门到进 ...

  4. Spring系列之手写一个SpringMVC

    目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 Spring系列之AOP的原理及手动实现 Spring系列之手写注解与配置文件的解析 引言 在前面的几个章节中我 ...

  5. 【Spring Boot&& Spring Cloud系列】单点登录SSO概述

    概念 单点登录(Singleton Sign On),简称为SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就能访问所有相互信任的应用系统. 也就 ...

  6. Asp.Net Core 轻松学系列-5利用 Swagger 自动生成接口文档

    目录 前言 结语 源码下载 前言     目前市场上主流的开发模式,几乎清一色的前后端分离方式,作为服务端开发人员,我们有义务提供给各个客户端良好的开发文档,以方便对接,减少沟通时间,提高开发效率:对 ...

  7. Asp.Net Core 轻松学系列-4玩转配置文件

    目录 前言 另类方式使用 hosting.json 使程序运行于多个端口 结语 前言     在 .NET Core 项目中,配置文件有着举足轻重的地位:与.NetFramework 不同的是,.NE ...

  8. Asp.Net Core 轻松学系列-2从安装环境开始

    Asp.Net Core 介绍     Asp.Net Core是微软新一代的跨平台开发框架,基于 C# 语言进行开发,该框架的推出,意味着微软从系统层面正式进击 Linux 服务器平台:从更新速度开 ...

  9. 跟我一起学Go系列:从写测试用例开始仗剑走天涯

    从入门到深入 Go 我们已经走了很长的路,当你想启动多个测试类的时候你是不是想启动多个 main 方法,但是 Go 限制了在同一个 package 下只能有一个 main,所以这条路你是走不通的.那我 ...

随机推荐

  1. Linux MySQL分库分表之Mycat

    介绍 背景 当表的个数达到了几百千万张表时,众多的业务模块都访问这个数据库,压力会比较大,考虑对其进行分库 当表的数据达到几千万级别,在做很多操作都比较吃力,考虑对其进行分库或分表 数据切分(shar ...

  2. javadoc导出成word文档

    刚刚上次弄完了一个坑爹的任务,这次我领导又给我一个让人脑瓜子疼的任务了. 基本上客户他在验收我们系统的时候,都会要求我们编写相关的文档,这次也不例外. 只是这次的客户要求我们给出接口文档.不仅是要整个 ...

  3. bugku社工writeup

    最近bugku的web和杂项刷了多半,突然心血来潮想试试社工题,bugku的社工题比较基础,而且题量不多,和大家分享一下writeup. 1.密码 根据提示,多猜几次密码就对了,然后得到flag. 2 ...

  4. 重学ASP.NET Core 中的标记帮助程序

    标记帮助程序是什么 标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素. 例如,内置的 ImageTagHelper 可以将版本号追加到图片名称.  每当图片发生变化时 ...

  5. 【原】二进制部署 k8s 1.18.3

    二进制部署 k8s 1.18.3 1.相关前置信息 1.1 版本信息 kube_version: v1.18.3 etcd_version: v3.4.9 flannel: v0.12.0 cored ...

  6. django admin 添加用户出现外键约束错误

    今天在做mxonline项目时,注册了用户表进admin后,想在后台添加一个用户试试,结果出现了错误,经过一番搜索发现以下两个解决方法,不过我只用了一种 报错信息: IntegrityError: ( ...

  7. Java 添加、下载、读取PDF附件信息(基于Spire.Cloud.SDK for Java)

    Spire.Cloud.SDK for Java提供了PdfAttachmentsApi接口添加附件addAttachment().下载附件downloadAttachment().获取附件信息get ...

  8. web前端知识点(JavaScript篇)

    call,apply,bind call,apply,bind这三者的区别,及内部实现原理,点这里 promise promise函数的内部实现原理,点这里 闭包 闭包就是能够读取其他函数内部变量的函 ...

  9. DP没入门就入土

    写在前面 记录最近刷的DP题 以及 打死都不可能想到状态设计DP系列 汇总 洛谷 P6082 [JSOI2015]salesman 树形\(\texttt{DP}\) + 优先队列 比较容易看出来这是 ...

  10. 洛谷P3295 [SCOI2016]萌萌哒 题解

    洛谷P3295 [SCOI2016]萌萌哒 题目描述 公式粘过来就乱了,还是去洛谷看题吧 分析 如果暴力解决的话就是使用并查集把位数相同的数位并在一起.比如区间[1,2]和区间[3,4]的数字完全相同 ...