意图

定义一个操作中的算法的骨架,将一些步骤延迟到子类中。 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. Spring系列.AOP使用

    AOP简介 利用面向对象的方法可以很好的组织代码,也可以继承的方式实现代码重用.但是项目中总是会出现一些重复的代码,并且不太方便使用继承的方式把他们重用管理起来,比如说通用日志打印,事务处理和安全检查 ...

  2. php 整数转罗马数字

    class Solution { /** * @param Integer $num * @return String */ function intToRoman($num) { $roman=[' ...

  3. Linux操作篇之配置DNS服务(一)

    一.什么是DN. 域名(Domain Name)又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识(有时也指地理位置).它的作用只 ...

  4. [ 头皮发麻 A1 ] 队内赛3 2020 Ateneo de Manila University DISCS PrO HS Division

    都是英语阅读题 但是本菜鸡就过了一题,直接自闭mmp明天开始起床一版题 传送门 B.Riana and the Blind Date 0是闰年?惊了 后来才知道整除被除数可以为0 闰年的计算方法 \( ...

  5. Linux远程连接mongodb

    当没有客户端工具,eg:robo3T时,如何修改mongodb里的数据呢? 1.连接mongodb服务器mongo 1.1.1.1:1688 2.查看数据库列表show dbs 3.选择使用log库u ...

  6. linux网络编程-socket(2)

    当客户端调用close函数的时候,服务器的read函数读到的数据是0读到文件结束通知,表示对端关闭了tcp连接 我们现实实现下面的功能: 1.tcp客户端从标准的输入流中得到输入数据发送到服务器,服务 ...

  7. Java 多线程基础(十一)线程优先级和守护线程

    Java 多线程基础(十一)线程优先级和守护线程 一.线程优先级 Java 提供了一个线程调度器来监控程序启动后进去就绪状态的所有线程.线程调度器通过线程的优先级来决定调度哪些线程执行.一般来说,Ja ...

  8. Linux下9种优秀的代码比对工具推荐

    大家好,我是良许. 在我们编写代码的时候,我们经常需要知道两个文件之间,或者同一个文件不同版本之间有什么差异性.在 Windows 下有个很强大的工具叫作 BeyondCompare ,那在 Linu ...

  9. java代理,静态代理、jdk代理、cglib代理、Aspectj

    我实在接触spring的时候才接触到代理这个东西的,一直想整理一下笔记. 什么是代理模式:代理模式是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能.简单来说就是要创建一个新的对象,我 ...

  10. Spring Cloud Alibaba系列(五)sentinel实现服务限流降级

    一.sentinel是什么 sentinel的官方名称叫分布式系统的流量防卫兵.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性.在Spring Clou ...