定义一个操作中的算法的骨架,将一些步骤延迟到子类中, Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
模板方法模式为我们提供了一种代码复用的重要技巧,它定义了算法的步骤,把这些步骤的实现延迟到子类
说人话就是:
【产品】:开发小哥,你会做饭吗?
【开发】:不会啊,怎么了?难道你要做给我吃?
【产品】:你想太多了,我就准备教你做一道炒包菜~,用你们写代码思路,我也来试试写写伪代码
public void cookie(){ // 第一步:倒油 this.pourOil(); // 第二步:热油 this.HeatOil(); // 第三步:倒入包菜 this.pourVegetable(); // 第四步:倒入调味料 this.pourSauce(); // 第五步:翻炒 this.fry(); } 复制代码
【开发】:我懂了,小姐姐,你要不来我家?我给你做一道炒蒜蓉~
【产品】:???你不是说你不会做饭吗?
【开发】:你不是写出来步骤了嘛,我已经会了,嘿嘿嘿~
❝你要是还没会,那咱们就一起看看核心代码吧~ 你如果会了,那直接跳到SSO实战吧~
❞
「抽象类」
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("下锅的酱料是蒜蓉"); } } 复制代码
模板方法模式的设计思路:
简单来说,
❝如果看着有点模棱两可,建议看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面
❞
要说到用模板方法模式去使用SSO时,咱们先需要知道什么是SSO(知道的同学直接略过啦~)
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; } } 复制代码
我们在编写业务代码的时候,完全依赖于顶层抽象类,彼时再动态的更改具体的实现类即可
动态指定实现类的最好方式无非这么几种,大家可以根据需求选择:
当我们使用模板方法模式,对整个SSO的流程已经梳理完成之后,第二个SSO只需要实现相应接口,然后根据客户的要求构建不同的加密协议,同时改一下配置即可,反正我只花了5分钟~
比如我们使用的所有APP,在进行支付的时候大多都可以选择支付宝或者微信支付,其实整个支付步骤中,只有具体支付的步骤是分为多种情况(微信,支付宝,银行卡等)剩下的订单推送,数据扭转等很有可能是一样的,此时我们就可以使用模板方法模式来约束行为,同时减少重复代码
PS:此种情况也可能使用策略模式处理,需要视情况而定
「附上GOF一书中对于模板方法模式的UML图:」
GitHub地址